summaryrefslogtreecommitdiff
path: root/tools/perf/scripts/python/flamegraph.py
diff options
context:
space:
mode:
authorAndrew Kilroy <andrew.kilroy@arm.com>2021-10-06 09:11:04 +0100
committerArnaldo Carvalho de Melo <acme@redhat.com>2021-10-20 11:22:11 -0300
commite166fc328b100b6e8ac7272b45d77494f79aab5f (patch)
tree9ef2cf4679c26faffc2b77cae641adace1f29ce5 /tools/perf/scripts/python/flamegraph.py
parent25bc4793dc89b845be8c1f1f37c00ee315363ce4 (diff)
perf vendor events arm64: Add new armv8 pmu events
Add new armv8 common events for use by Arm Neoverse V1 cores in a later commit. These are defined in the ArmV8 architecture reference manual available from https://developer.arm.com/documentation/ddi0487/gb/?lang=en Reviewed-by: John Garry <john.garry@huawei.com> Signed-off-by: Andrew Kilroy <andrew.kilroy@arm.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Leo Yan <leo.yan@linaro.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Mathieu Poirier <mathieu.poirier@linaro.org> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Will Deacon <will@kernel.org> Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20211006081106.8649-2-andrew.kilroy@arm.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/scripts/python/flamegraph.py')
0 files changed, 0 insertions, 0 deletions
class='mode'>-rw-r--r--sound/soc/codecs/88pm860x-codec.c279
-rw-r--r--sound/soc/codecs/88pm860x-codec.h9
-rw-r--r--sound/soc/codecs/Kconfig2365
-rw-r--r--sound/soc/codecs/Makefile887
-rw-r--r--sound/soc/codecs/ab8500-codec.c409
-rw-r--r--sound/soc/codecs/ab8500-codec.h7
-rw-r--r--sound/soc/codecs/ac97.c71
-rw-r--r--sound/soc/codecs/ad1836.c79
-rw-r--r--sound/soc/codecs/ad1836.h3
-rw-r--r--sound/soc/codecs/ad193x-i2c.c23
-rw-r--r--sound/soc/codecs/ad193x-spi.c25
-rw-r--r--sound/soc/codecs/ad193x.c318
-rw-r--r--sound/soc/codecs/ad193x.h24
-rw-r--r--sound/soc/codecs/ad1980.c126
-rw-r--r--sound/soc/codecs/ad73311.c32
-rw-r--r--sound/soc/codecs/ad73311.h17
-rw-r--r--sound/soc/codecs/adau-utils.c60
-rw-r--r--sound/soc/codecs/adau-utils.h8
-rw-r--r--sound/soc/codecs/adau1372-i2c.c41
-rw-r--r--sound/soc/codecs/adau1372-spi.c59
-rw-r--r--sound/soc/codecs/adau1372.c1073
-rw-r--r--sound/soc/codecs/adau1372.h22
-rw-r--r--sound/soc/codecs/adau1373.c369
-rw-r--r--sound/soc/codecs/adau1373.h1
-rw-r--r--sound/soc/codecs/adau1701.c282
-rw-r--r--sound/soc/codecs/adau1701.h3
-rw-r--r--sound/soc/codecs/adau1761-i2c.c28
-rw-r--r--sound/soc/codecs/adau1761-spi.c23
-rw-r--r--sound/soc/codecs/adau1761.c339
-rw-r--r--sound/soc/codecs/adau1761.h3
-rw-r--r--sound/soc/codecs/adau1781-i2c.c24
-rw-r--r--sound/soc/codecs/adau1781-spi.c19
-rw-r--r--sound/soc/codecs/adau1781.c63
-rw-r--r--sound/soc/codecs/adau1781.h3
-rw-r--r--sound/soc/codecs/adau17x1.c508
-rw-r--r--sound/soc/codecs/adau17x1.h27
-rw-r--r--sound/soc/codecs/adau1977-i2c.c16
-rw-r--r--sound/soc/codecs/adau1977-spi.c21
-rw-r--r--sound/soc/codecs/adau1977.c144
-rw-r--r--sound/soc/codecs/adau1977.h3
-rw-r--r--sound/soc/codecs/adau7002.c131
-rw-r--r--sound/soc/codecs/adau7118-hw.c43
-rw-r--r--sound/soc/codecs/adau7118-i2c.c88
-rw-r--r--sound/soc/codecs/adau7118.c576
-rw-r--r--sound/soc/codecs/adau7118.h24
-rw-r--r--sound/soc/codecs/adav801.c11
-rw-r--r--sound/soc/codecs/adav803.c16
-rw-r--r--sound/soc/codecs/adav80x.c158
-rw-r--r--sound/soc/codecs/adav80x.h3
-rw-r--r--sound/soc/codecs/ads117x.c40
-rw-r--r--sound/soc/codecs/ak4104.c104
-rw-r--r--sound/soc/codecs/ak4118.c416
-rw-r--r--sound/soc/codecs/ak4375.c606
-rw-r--r--sound/soc/codecs/ak4458.c813
-rw-r--r--sound/soc/codecs/ak4458.h90
-rw-r--r--sound/soc/codecs/ak4535.c95
-rw-r--r--sound/soc/codecs/ak4535.h5
-rw-r--r--sound/soc/codecs/ak4554.c45
-rw-r--r--sound/soc/codecs/ak4613.c927
-rw-r--r--sound/soc/codecs/ak4619.c912
-rw-r--r--sound/soc/codecs/ak4641.c130
-rw-r--r--sound/soc/codecs/ak4641.h47
-rw-r--r--sound/soc/codecs/ak4642.c360
-rw-r--r--sound/soc/codecs/ak4671.c128
-rw-r--r--sound/soc/codecs/ak4671.h7
-rw-r--r--sound/soc/codecs/ak5386.c95
-rw-r--r--sound/soc/codecs/ak5558.c508
-rw-r--r--sound/soc/codecs/ak5558.h52
-rw-r--r--sound/soc/codecs/alc5623.c205
-rw-r--r--sound/soc/codecs/alc5623.h6
-rw-r--r--sound/soc/codecs/alc5632.c188
-rw-r--r--sound/soc/codecs/alc5632.h5
-rw-r--r--sound/soc/codecs/arizona-jack.c1670
-rw-r--r--sound/soc/codecs/arizona.c1295
-rw-r--r--sound/soc/codecs/arizona.h224
-rw-r--r--sound/soc/codecs/audio-iio-aux.c314
-rw-r--r--sound/soc/codecs/aw8738.c104
-rw-r--r--sound/soc/codecs/aw87390.c461
-rw-r--r--sound/soc/codecs/aw87390.h85
-rw-r--r--sound/soc/codecs/aw88081.c1317
-rw-r--r--sound/soc/codecs/aw88081.h329
-rw-r--r--sound/soc/codecs/aw88166.c1928
-rw-r--r--sound/soc/codecs/aw88166.h534
-rw-r--r--sound/soc/codecs/aw88261.c1282
-rw-r--r--sound/soc/codecs/aw88261.h459
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c576
-rw-r--r--sound/soc/codecs/aw88395/aw88395.h58
-rw-r--r--sound/soc/codecs/aw88395/aw88395_data_type.h142
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c1720
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h214
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c1174
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.h92
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h383
-rw-r--r--sound/soc/codecs/aw88399.c2350
-rw-r--r--sound/soc/codecs/aw88399.h634
-rw-r--r--sound/soc/codecs/bd28623.c241
-rw-r--r--sound/soc/codecs/bt-sco.c102
-rw-r--r--sound/soc/codecs/chv3-codec.c42
-rw-r--r--sound/soc/codecs/cirrus_legacy.h21
-rw-r--r--sound/soc/codecs/cpcap.c1887
-rw-r--r--sound/soc/codecs/cq93vc.c65
-rw-r--r--sound/soc/codecs/cros_ec_codec.c1066
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c1002
-rw-r--r--sound/soc/codecs/cs-amp-lib.c415
-rw-r--r--sound/soc/codecs/cs35l32.c216
-rw-r--r--sound/soc/codecs/cs35l32.h8
-rw-r--r--sound/soc/codecs/cs35l33.c1287
-rw-r--r--sound/soc/codecs/cs35l33.h217
-rw-r--r--sound/soc/codecs/cs35l34.c1217
-rw-r--r--sound/soc/codecs/cs35l34.h265
-rw-r--r--sound/soc/codecs/cs35l35.c1662
-rw-r--r--sound/soc/codecs/cs35l35.h297
-rw-r--r--sound/soc/codecs/cs35l36.c1942
-rw-r--r--sound/soc/codecs/cs35l36.h446
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c96
-rw-r--r--sound/soc/codecs/cs35l41-lib.c1592
-rw-r--r--sound/soc/codecs/cs35l41-spi.c99
-rw-r--r--sound/soc/codecs/cs35l41.c1492
-rw-r--r--sound/soc/codecs/cs35l41.h41
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c76
-rw-r--r--sound/soc/codecs/cs35l45-spi.c78
-rw-r--r--sound/soc/codecs/cs35l45-tables.c332
-rw-r--r--sound/soc/codecs/cs35l45.c1515
-rw-r--r--sound/soc/codecs/cs35l45.h514
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c108
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c606
-rw-r--r--sound/soc/codecs/cs35l56-shared.c1355
-rw-r--r--sound/soc/codecs/cs35l56-spi.c95
-rw-r--r--sound/soc/codecs/cs35l56.c1564
-rw-r--r--sound/soc/codecs/cs35l56.h71
-rw-r--r--sound/soc/codecs/cs40l50-codec.c307
-rw-r--r--sound/soc/codecs/cs4234.c916
-rw-r--r--sound/soc/codecs/cs4234.h287
-rw-r--r--sound/soc/codecs/cs4265.c191
-rw-r--r--sound/soc/codecs/cs4265.h6
-rw-r--r--sound/soc/codecs/cs4270.c218
-rw-r--r--sound/soc/codecs/cs4271-i2c.c25
-rw-r--r--sound/soc/codecs/cs4271-spi.c20
-rw-r--r--sound/soc/codecs/cs4271.c217
-rw-r--r--sound/soc/codecs/cs4271.h1
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c104
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c625
-rw-r--r--sound/soc/codecs/cs42l42.c2495
-rw-r--r--sound/soc/codecs/cs42l42.h82
-rw-r--r--sound/soc/codecs/cs42l43-jack.c962
-rw-r--r--sound/soc/codecs/cs42l43-sdw.c71
-rw-r--r--sound/soc/codecs/cs42l43.c2476
-rw-r--r--sound/soc/codecs/cs42l43.h146
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c35
-rw-r--r--sound/soc/codecs/cs42l51.c415
-rw-r--r--sound/soc/codecs/cs42l51.h15
-rw-r--r--sound/soc/codecs/cs42l52.c377
-rw-r--r--sound/soc/codecs/cs42l52.h6
-rw-r--r--sound/soc/codecs/cs42l56.c406
-rw-r--r--sound/soc/codecs/cs42l56.h6
-rw-r--r--sound/soc/codecs/cs42l73.c373
-rw-r--r--sound/soc/codecs/cs42l73.h16
-rw-r--r--sound/soc/codecs/cs42l83-i2c.c240
-rw-r--r--sound/soc/codecs/cs42l84.c1111
-rw-r--r--sound/soc/codecs/cs42l84.h210
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c34
-rw-r--r--sound/soc/codecs/cs42xx8.c256
-rw-r--r--sound/soc/codecs/cs42xx8.h2
-rw-r--r--sound/soc/codecs/cs43130.c2781
-rw-r--r--sound/soc/codecs/cs43130.h541
-rw-r--r--sound/soc/codecs/cs4341.c352
-rw-r--r--sound/soc/codecs/cs4349.c382
-rw-r--r--sound/soc/codecs/cs4349.h127
-rw-r--r--sound/soc/codecs/cs47l15.c1506
-rw-r--r--sound/soc/codecs/cs47l24.c1355
-rw-r--r--sound/soc/codecs/cs47l24.h20
-rw-r--r--sound/soc/codecs/cs47l35.c1781
-rw-r--r--sound/soc/codecs/cs47l85.c2732
-rw-r--r--sound/soc/codecs/cs47l90.c2656
-rw-r--r--sound/soc/codecs/cs47l92.c2104
-rw-r--r--sound/soc/codecs/cs48l32-tables.c538
-rw-r--r--sound/soc/codecs/cs48l32.c4076
-rw-r--r--sound/soc/codecs/cs48l32.h403
-rw-r--r--sound/soc/codecs/cs530x-i2c.c72
-rw-r--r--sound/soc/codecs/cs530x.c971
-rw-r--r--sound/soc/codecs/cs530x.h223
-rw-r--r--sound/soc/codecs/cs53l30.c1106
-rw-r--r--sound/soc/codecs/cs53l30.h455
-rw-r--r--sound/soc/codecs/cx20442.c174
-rw-r--r--sound/soc/codecs/cx20442.h7
-rw-r--r--sound/soc/codecs/cx2072x.c1718
-rw-r--r--sound/soc/codecs/cx2072x.h314
-rw-r--r--sound/soc/codecs/da7210.c254
-rw-r--r--sound/soc/codecs/da7213.c1113
-rw-r--r--sound/soc/codecs/da7213.h138
-rw-r--r--sound/soc/codecs/da7218.c3303
-rw-r--r--sound/soc/codecs/da7218.h1411
-rw-r--r--sound/soc/codecs/da7219-aad.c1080
-rw-r--r--sound/soc/codecs/da7219-aad.h222
-rw-r--r--sound/soc/codecs/da7219.c2732
-rw-r--r--sound/soc/codecs/da7219.h839
-rw-r--r--sound/soc/codecs/da732x.c296
-rw-r--r--sound/soc/codecs/da732x.h17
-rw-r--r--sound/soc/codecs/da732x_reg.h5
-rw-r--r--sound/soc/codecs/da9055.c221
-rw-r--r--sound/soc/codecs/dmic.c192
-rw-r--r--sound/soc/codecs/es7134.c316
-rw-r--r--sound/soc/codecs/es7241.c311
-rw-r--r--sound/soc/codecs/es8311.c973
-rw-r--r--sound/soc/codecs/es8311.h162
-rw-r--r--sound/soc/codecs/es8316.c933
-rw-r--r--sound/soc/codecs/es8316.h135
-rw-r--r--sound/soc/codecs/es8323.c791
-rw-r--r--sound/soc/codecs/es8323.h78
-rw-r--r--sound/soc/codecs/es8326.c1374
-rw-r--r--sound/soc/codecs/es8326.h200
-rw-r--r--sound/soc/codecs/es8328-i2c.c21
-rw-r--r--sound/soc/codecs/es8328-spi.c12
-rw-r--r--sound/soc/codecs/es8328.c424
-rw-r--r--sound/soc/codecs/es8328.h62
-rw-r--r--sound/soc/codecs/es8375.c794
-rw-r--r--sound/soc/codecs/es8375.h123
-rw-r--r--sound/soc/codecs/es8389.c962
-rw-r--r--sound/soc/codecs/es8389.h140
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.c89
-rw-r--r--sound/soc/codecs/es83xx-dsm-common.h393
-rw-r--r--sound/soc/codecs/framer-codec.c413
-rw-r--r--sound/soc/codecs/fs-amp-lib.c265
-rw-r--r--sound/soc/codecs/fs-amp-lib.h150
-rw-r--r--sound/soc/codecs/fs210x.c1586
-rw-r--r--sound/soc/codecs/fs210x.h75
-rw-r--r--sound/soc/codecs/gtm601.c109
-rw-r--r--sound/soc/codecs/hda-dai.c105
-rw-r--r--sound/soc/codecs/hda.c396
-rw-r--r--sound/soc/codecs/hda.h19
-rw-r--r--sound/soc/codecs/hdac_hda.c679
-rw-r--r--sound/soc/codecs/hdac_hda.h34
-rw-r--r--sound/soc/codecs/hdac_hdmi.c2043
-rw-r--r--sound/soc/codecs/hdmi-codec.c1212
-rw-r--r--sound/soc/codecs/hdmi.c109
-rw-r--r--sound/soc/codecs/ics43432.c74
-rw-r--r--sound/soc/codecs/idt821034.c1183
-rw-r--r--sound/soc/codecs/inno_rk3036.c486
-rw-r--r--sound/soc/codecs/inno_rk3036.h124
-rw-r--r--sound/soc/codecs/isabelle.c100
-rw-r--r--sound/soc/codecs/isabelle.h6
-rw-r--r--sound/soc/codecs/jz4725b.c667
-rw-r--r--sound/soc/codecs/jz4740.c105
-rw-r--r--sound/soc/codecs/jz4760.c871
-rw-r--r--sound/soc/codecs/jz4770.c919
-rw-r--r--sound/soc/codecs/l3.c91
-rw-r--r--sound/soc/codecs/lm4857.c127
-rw-r--r--sound/soc/codecs/lm49453.c138
-rw-r--r--sound/soc/codecs/lm49453.h6
-rw-r--r--sound/soc/codecs/lochnagar-sc.c266
-rw-r--r--sound/soc/codecs/lpass-macro-common.c93
-rw-r--r--sound/soc/codecs/lpass-macro-common.h77
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c4039
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c2542
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1747
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c3048
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.h17
-rw-r--r--sound/soc/codecs/madera.c4812
-rw-r--r--sound/soc/codecs/madera.h458
-rw-r--r--sound/soc/codecs/max9759.c198
-rw-r--r--sound/soc/codecs/max9768.c129
-rw-r--r--sound/soc/codecs/max98088.c892
-rw-r--r--sound/soc/codecs/max98088.h7
-rw-r--r--sound/soc/codecs/max98090.c730
-rw-r--r--sound/soc/codecs/max98090.h14
-rw-r--r--sound/soc/codecs/max98095.c882
-rw-r--r--sound/soc/codecs/max98095.h7
-rw-r--r--sound/soc/codecs/max98357a.c152
-rw-r--r--sound/soc/codecs/max98363.c465
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98371.c430
-rw-r--r--sound/soc/codecs/max98371.h63
-rw-r--r--sound/soc/codecs/max98373-i2c.c616
-rw-r--r--sound/soc/codecs/max98373-sdw.c888
-rw-r--r--sound/soc/codecs/max98373-sdw.h72
-rw-r--r--sound/soc/codecs/max98373.c509
-rw-r--r--sound/soc/codecs/max98373.h240
-rw-r--r--sound/soc/codecs/max98388.c1013
-rw-r--r--sound/soc/codecs/max98388.h234
-rw-r--r--sound/soc/codecs/max98390.c1141
-rw-r--r--sound/soc/codecs/max98390.h667
-rw-r--r--sound/soc/codecs/max98396.c1915
-rw-r--r--sound/soc/codecs/max98396.h327
-rw-r--r--sound/soc/codecs/max9850.c125
-rw-r--r--sound/soc/codecs/max9850.h7
-rw-r--r--sound/soc/codecs/max98504.c382
-rw-r--r--sound/soc/codecs/max98504.h56
-rw-r--r--sound/soc/codecs/max98520.c766
-rw-r--r--sound/soc/codecs/max98520.h159
-rw-r--r--sound/soc/codecs/max9860.c738
-rw-r--r--sound/soc/codecs/max9860.h154
-rw-r--r--sound/soc/codecs/max9867.c713
-rw-r--r--sound/soc/codecs/max9867.h67
-rw-r--r--sound/soc/codecs/max9877.c45
-rw-r--r--sound/soc/codecs/max9877.h9
-rw-r--r--sound/soc/codecs/max98925.c646
-rw-r--r--sound/soc/codecs/max98925.h829
-rw-r--r--sound/soc/codecs/max98926.c593
-rw-r--r--sound/soc/codecs/max98926.h846
-rw-r--r--sound/soc/codecs/max98927.c914
-rw-r--r--sound/soc/codecs/max98927.h271
-rw-r--r--sound/soc/codecs/mc13783.c120
-rw-r--r--sound/soc/codecs/mc13783.h14
-rw-r--r--sound/soc/codecs/ml26124.c184
-rw-r--r--sound/soc/codecs/ml26124.h14
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c1280
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c1251
-rw-r--r--sound/soc/codecs/mt6351.c1496
-rw-r--r--sound/soc/codecs/mt6351.h105
-rw-r--r--sound/soc/codecs/mt6357.c1855
-rw-r--r--sound/soc/codecs/mt6357.h660
-rw-r--r--sound/soc/codecs/mt6358.c2431
-rw-r--r--sound/soc/codecs/mt6358.h2310
-rw-r--r--sound/soc/codecs/mt6359-accdet.c1062
-rw-r--r--sound/soc/codecs/mt6359-accdet.h137
-rw-r--r--sound/soc/codecs/mt6359.c2962
-rw-r--r--sound/soc/codecs/mt6359.h4289
-rw-r--r--sound/soc/codecs/mt6660.c581
-rw-r--r--sound/soc/codecs/mt6660.h77
-rw-r--r--sound/soc/codecs/nau8315.c167
-rw-r--r--sound/soc/codecs/nau8325.c900
-rw-r--r--sound/soc/codecs/nau8325.h391
-rw-r--r--sound/soc/codecs/nau8540.c993
-rw-r--r--sound/soc/codecs/nau8540.h265
-rw-r--r--sound/soc/codecs/nau8810.c928
-rw-r--r--sound/soc/codecs/nau8810.h286
-rw-r--r--sound/soc/codecs/nau8821.c1959
-rw-r--r--sound/soc/codecs/nau8821.h586
-rw-r--r--sound/soc/codecs/nau8822.c1221
-rw-r--r--sound/soc/codecs/nau8822.h224
-rw-r--r--sound/soc/codecs/nau8824.c2058
-rw-r--r--sound/soc/codecs/nau8824.h478
-rw-r--r--sound/soc/codecs/nau8825.c2972
-rw-r--r--sound/soc/codecs/nau8825.h538
-rw-r--r--sound/soc/codecs/ntp8835.c480
-rw-r--r--sound/soc/codecs/ntp8918.c396
-rw-r--r--sound/soc/codecs/ntpfw.c137
-rw-r--r--sound/soc/codecs/ntpfw.h23
-rw-r--r--sound/soc/codecs/pcm1681.c99
-rw-r--r--sound/soc/codecs/pcm1754.c185
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c63
-rw-r--r--sound/soc/codecs/pcm1789.c271
-rw-r--r--sound/soc/codecs/pcm1789.h17
-rw-r--r--sound/soc/codecs/pcm1792a.c272
-rw-r--r--sound/soc/codecs/pcm1792a.h27
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c59
-rw-r--r--sound/soc/codecs/pcm179x-spi.c58
-rw-r--r--sound/soc/codecs/pcm179x.c231
-rw-r--r--sound/soc/codecs/pcm179x.h18
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c60
-rw-r--r--sound/soc/codecs/pcm186x-spi.c61
-rw-r--r--sound/soc/codecs/pcm186x.c704
-rw-r--r--sound/soc/codecs/pcm186x.h219
-rw-r--r--sound/soc/codecs/pcm3008.c94
-rw-r--r--sound/soc/codecs/pcm3008.h22
-rw-r--r--sound/soc/codecs/pcm3060-i2c.c59
-rw-r--r--sound/soc/codecs/pcm3060-spi.c59
-rw-r--r--sound/soc/codecs/pcm3060.c347
-rw-r--r--sound/soc/codecs/pcm3060.h96
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c69
-rw-r--r--sound/soc/codecs/pcm3168a-spi.c60
-rw-r--r--sound/soc/codecs/pcm3168a.c914
-rw-r--r--sound/soc/codecs/pcm3168a.h97
-rw-r--r--sound/soc/codecs/pcm5102a.c57
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c48
-rw-r--r--sound/soc/codecs/pcm512x-spi.c24
-rw-r--r--sound/soc/codecs/pcm512x.c705
-rw-r--r--sound/soc/codecs/pcm512x.h14
-rw-r--r--sound/soc/codecs/pcm6240.c2170
-rw-r--r--sound/soc/codecs/pcm6240.h247
-rw-r--r--sound/soc/codecs/peb2466.c2064
-rw-r--r--sound/soc/codecs/pm4125-sdw.c545
-rw-r--r--sound/soc/codecs/pm4125.c1780
-rw-r--r--sound/soc/codecs/pm4125.h307
-rw-r--r--sound/soc/codecs/rk3308_codec.c974
-rw-r--r--sound/soc/codecs/rk3308_codec.h579
-rw-r--r--sound/soc/codecs/rk3328_codec.c534
-rw-r--r--sound/soc/codecs/rk3328_codec.h210
-rw-r--r--sound/soc/codecs/rk817_codec.c540
-rw-r--r--sound/soc/codecs/rl6231.c218
-rw-r--r--sound/soc/codecs/rl6231.h9
-rw-r--r--sound/soc/codecs/rl6347a.c108
-rw-r--r--sound/soc/codecs/rl6347a.h31
-rw-r--r--sound/soc/codecs/rt-sdw-common.c238
-rw-r--r--sound/soc/codecs/rt-sdw-common.h66
-rw-r--r--sound/soc/codecs/rt1011.c2492
-rw-r--r--sound/soc/codecs/rt1011.h704
-rw-r--r--sound/soc/codecs/rt1015.c1193
-rw-r--r--sound/soc/codecs/rt1015.h449
-rw-r--r--sound/soc/codecs/rt1015p.c154
-rw-r--r--sound/soc/codecs/rt1016.c693
-rw-r--r--sound/soc/codecs/rt1016.h232
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c822
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.h183
-rw-r--r--sound/soc/codecs/rt1019.c609
-rw-r--r--sound/soc/codecs/rt1019.h164
-rw-r--r--sound/soc/codecs/rt1305.c1179
-rw-r--r--sound/soc/codecs/rt1305.h273
-rw-r--r--sound/soc/codecs/rt1308-sdw.c817
-rw-r--r--sound/soc/codecs/rt1308-sdw.h172
-rw-r--r--sound/soc/codecs/rt1308.c871
-rw-r--r--sound/soc/codecs/rt1308.h294
-rw-r--r--sound/soc/codecs/rt1316-sdw.c794
-rw-r--r--sound/soc/codecs/rt1316-sdw.h52
-rw-r--r--sound/soc/codecs/rt1318-sdw.c868
-rw-r--r--sound/soc/codecs/rt1318-sdw.h96
-rw-r--r--sound/soc/codecs/rt1318.c1353
-rw-r--r--sound/soc/codecs/rt1318.h342
-rw-r--r--sound/soc/codecs/rt1320-sdw.c1823
-rw-r--r--sound/soc/codecs/rt1320-sdw.h113
-rw-r--r--sound/soc/codecs/rt274.c1233
-rw-r--r--sound/soc/codecs/rt274.h214
-rw-r--r--sound/soc/codecs/rt286.c498
-rw-r--r--sound/soc/codecs/rt286.h7
-rw-r--r--sound/soc/codecs/rt298.c1323
-rw-r--r--sound/soc/codecs/rt298.h211
-rw-r--r--sound/soc/codecs/rt5514-spi.c514
-rw-r--r--sound/soc/codecs/rt5514-spi.h37
-rw-r--r--sound/soc/codecs/rt5514.c1333
-rw-r--r--sound/soc/codecs/rt5514.h286
-rw-r--r--sound/soc/codecs/rt5616.c1413
-rw-r--r--sound/soc/codecs/rt5616.h1816
-rw-r--r--sound/soc/codecs/rt5631.c385
-rw-r--r--sound/soc/codecs/rt5631.h1
-rw-r--r--sound/soc/codecs/rt5640.c1371
-rw-r--r--sound/soc/codecs/rt5640.h154
-rw-r--r--sound/soc/codecs/rt5645.c2492
-rw-r--r--sound/soc/codecs/rt5645.h129
-rw-r--r--sound/soc/codecs/rt5651.c897
-rw-r--r--sound/soc/codecs/rt5651.h38
-rw-r--r--sound/soc/codecs/rt5659.c4346
-rw-r--r--sound/soc/codecs/rt5659.h1821
-rw-r--r--sound/soc/codecs/rt5660.c1349
-rw-r--r--sound/soc/codecs/rt5660.h847
-rw-r--r--sound/soc/codecs/rt5663.c3756
-rw-r--r--sound/soc/codecs/rt5663.h1128
-rw-r--r--sound/soc/codecs/rt5665.c4868
-rw-r--r--sound/soc/codecs/rt5665.h2002
-rw-r--r--sound/soc/codecs/rt5668.c2585
-rw-r--r--sound/soc/codecs/rt5668.h1312
-rw-r--r--sound/soc/codecs/rt5670-dsp.h5
-rw-r--r--sound/soc/codecs/rt5670.c1029
-rw-r--r--sound/soc/codecs/rt5670.h65
-rw-r--r--sound/soc/codecs/rt5677-spi.c632
-rw-r--r--sound/soc/codecs/rt5677-spi.h30
-rw-r--r--sound/soc/codecs/rt5677.c1652
-rw-r--r--sound/soc/codecs/rt5677.h285
-rw-r--r--sound/soc/codecs/rt5682-i2c.c348
-rw-r--r--sound/soc/codecs/rt5682-sdw.c811
-rw-r--r--sound/soc/codecs/rt5682.c3191
-rw-r--r--sound/soc/codecs/rt5682.h1499
-rw-r--r--sound/soc/codecs/rt5682s.c3348
-rw-r--r--sound/soc/codecs/rt5682s.h1492
-rw-r--r--sound/soc/codecs/rt700-sdw.c572
-rw-r--r--sound/soc/codecs/rt700-sdw.h335
-rw-r--r--sound/soc/codecs/rt700.c1241
-rw-r--r--sound/soc/codecs/rt700.h171
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c496
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt711-sdca.c1672
-rw-r--r--sound/soc/codecs/rt711-sdca.h244
-rw-r--r--sound/soc/codecs/rt711-sdw.c585
-rw-r--r--sound/soc/codecs/rt711-sdw.h283
-rw-r--r--sound/soc/codecs/rt711.c1341
-rw-r--r--sound/soc/codecs/rt711.h251
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c987
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h107
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c510
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h75
-rw-r--r--sound/soc/codecs/rt712-sdca.c1932
-rw-r--r--sound/soc/codecs/rt712-sdca.h261
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c284
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.h171
-rw-r--r--sound/soc/codecs/rt715-sdca.c1075
-rw-r--r--sound/soc/codecs/rt715-sdca.h132
-rw-r--r--sound/soc/codecs/rt715-sdw.c551
-rw-r--r--sound/soc/codecs/rt715-sdw.h337
-rw-r--r--sound/soc/codecs/rt715.c1140
-rw-r--r--sound/soc/codecs/rt715.h224
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c548
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.h150
-rw-r--r--sound/soc/codecs/rt721-sdca.c1560
-rw-r--r--sound/soc/codecs/rt721-sdca.h273
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c555
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h122
-rw-r--r--sound/soc/codecs/rt722-sdca.c1583
-rw-r--r--sound/soc/codecs/rt722-sdca.h250
-rw-r--r--sound/soc/codecs/rt9120.c643
-rw-r--r--sound/soc/codecs/rt9123.c500
-rw-r--r--sound/soc/codecs/rt9123p.c171
-rw-r--r--sound/soc/codecs/rtq9124.c543
-rw-r--r--sound/soc/codecs/rtq9128.c789
-rw-r--r--sound/soc/codecs/sdw-mockup.c275
-rw-r--r--sound/soc/codecs/sgtl5000.c1217
-rw-r--r--sound/soc/codecs/sgtl5000.h18
-rw-r--r--sound/soc/codecs/si476x.c61
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c10
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c5
-rw-r--r--sound/soc/codecs/sigmadsp.c61
-rw-r--r--sound/soc/codecs/sigmadsp.h6
-rw-r--r--sound/soc/codecs/simple-amplifier.c110
-rw-r--r--sound/soc/codecs/simple-mux.c191
-rw-r--r--sound/soc/codecs/sirf-audio-codec.c581
-rw-r--r--sound/soc/codecs/sirf-audio-codec.h125
-rw-r--r--sound/soc/codecs/sma1303.c1820
-rw-r--r--sound/soc/codecs/sma1303.h609
-rw-r--r--sound/soc/codecs/sma1307.c2065
-rw-r--r--sound/soc/codecs/sma1307.h444
-rw-r--r--sound/soc/codecs/sn95031.c928
-rw-r--r--sound/soc/codecs/sn95031.h132
-rw-r--r--sound/soc/codecs/spdif_receiver.c34
-rw-r--r--sound/soc/codecs/spdif_transmitter.c34
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c46
-rw-r--r--sound/soc/codecs/src4xxx.c518
-rw-r--r--sound/soc/codecs/src4xxx.h113
-rw-r--r--sound/soc/codecs/ssm2305.c99
-rw-r--r--sound/soc/codecs/ssm2518.c128
-rw-r--r--sound/soc/codecs/ssm2518.h3
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c16
-rw-r--r--sound/soc/codecs/ssm2602-spi.c11
-rw-r--r--sound/soc/codecs/ssm2602.c217
-rw-r--r--sound/soc/codecs/ssm2602.h16
-rw-r--r--sound/soc/codecs/ssm3515.c448
-rw-r--r--sound/soc/codecs/ssm4567.c121
-rw-r--r--sound/soc/codecs/sta32x.c261
-rw-r--r--sound/soc/codecs/sta32x.h6
-rw-r--r--sound/soc/codecs/sta350.c244
-rw-r--r--sound/soc/codecs/sta350.h8
-rw-r--r--sound/soc/codecs/sta529.c95
-rw-r--r--sound/soc/codecs/stac9766.c281
-rw-r--r--sound/soc/codecs/stac9766.h17
-rw-r--r--sound/soc/codecs/sti-sas.c465
-rw-r--r--sound/soc/codecs/tas2552.c624
-rw-r--r--sound/soc/codecs/tas2552.h167
-rw-r--r--sound/soc/codecs/tas2562.c781
-rw-r--r--sound/soc/codecs/tas2562.h90
-rw-r--r--sound/soc/codecs/tas2764-quirks.h180
-rw-r--r--sound/soc/codecs/tas2764.c950
-rw-r--r--sound/soc/codecs/tas2764.h129
-rw-r--r--sound/soc/codecs/tas2770.c890
-rw-r--r--sound/soc/codecs/tas2770.h151
-rw-r--r--sound/soc/codecs/tas2780.c652
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c371
-rw-r--r--sound/soc/codecs/tas2781-comlib.c221
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c2618
-rw-r--r--sound/soc/codecs/tas2781-i2c.c2084
-rw-r--r--sound/soc/codecs/tas2783-sdw.c1331
-rw-r--r--sound/soc/codecs/tas2783.h110
-rw-r--r--sound/soc/codecs/tas5086.c154
-rw-r--r--sound/soc/codecs/tas571x.c1090
-rw-r--r--sound/soc/codecs/tas571x.h141
-rw-r--r--sound/soc/codecs/tas5720.c827
-rw-r--r--sound/soc/codecs/tas5720.h127
-rw-r--r--sound/soc/codecs/tas5805m.c613
-rw-r--r--sound/soc/codecs/tas6424.c814
-rw-r--r--sound/soc/codecs/tas6424.h158
-rw-r--r--sound/soc/codecs/tda7419.c641
-rw-r--r--sound/soc/codecs/tfa9879.c119
-rw-r--r--sound/soc/codecs/tfa9879.h7
-rw-r--r--sound/soc/codecs/tfa989x.c425
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c1522
-rw-r--r--sound/soc/codecs/tlv320adcx140.c1217
-rw-r--r--sound/soc/codecs/tlv320adcx140.h159
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c19
-rw-r--r--sound/soc/codecs/tlv320aic23-spi.c13
-rw-r--r--sound/soc/codecs/tlv320aic23.c145
-rw-r--r--sound/soc/codecs/tlv320aic23.h5
-rw-r--r--sound/soc/codecs/tlv320aic26.c118
-rw-r--r--sound/soc/codecs/tlv320aic26.h7
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c1284
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h387
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c499
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4-spi.c70
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c1395
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h348
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c72
-rw-r--r--sound/soc/codecs/tlv320aic3x-spi.c77
-rw-r--r--sound/soc/codecs/tlv320aic3x.c673
-rw-r--r--sound/soc/codecs/tlv320aic3x.h69
-rw-r--r--sound/soc/codecs/tlv320dac33.c557
-rw-r--r--sound/soc/codecs/tlv320dac33.h16
-rw-r--r--sound/soc/codecs/tpa6130a2.c485
-rw-r--r--sound/soc/codecs/tpa6130a2.h30
-rw-r--r--sound/soc/codecs/ts3a227e.c165
-rw-r--r--sound/soc/codecs/ts3a227e.h5
-rw-r--r--sound/soc/codecs/tscs42xx.c1514
-rw-r--r--sound/soc/codecs/tscs42xx.h2701
-rw-r--r--sound/soc/codecs/tscs454.c3483
-rw-r--r--sound/soc/codecs/tscs454.h2323
-rw-r--r--sound/soc/codecs/twl4030.c578
-rw-r--r--sound/soc/codecs/twl6040.c325
-rw-r--r--sound/soc/codecs/twl6040.h26
-rw-r--r--sound/soc/codecs/uda1334.c294
-rw-r--r--sound/soc/codecs/uda1342.c347
-rw-r--r--sound/soc/codecs/uda1342.h78
-rw-r--r--sound/soc/codecs/uda134x.c617
-rw-r--r--sound/soc/codecs/uda134x.h34
-rw-r--r--sound/soc/codecs/uda1380.c312
-rw-r--r--sound/soc/codecs/uda1380.h9
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.c905
-rw-r--r--sound/soc/codecs/wcd-clsh-v2.h66
-rw-r--r--sound/soc/codecs/wcd-common.c144
-rw-r--r--sound/soc/codecs/wcd-common.h46
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c1647
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h343
-rw-r--r--sound/soc/codecs/wcd9335.c5164
-rw-r--r--sound/soc/codecs/wcd9335.h641
-rw-r--r--sound/soc/codecs/wcd934x.c5917
-rw-r--r--sound/soc/codecs/wcd937x-sdw.c1115
-rw-r--r--sound/soc/codecs/wcd937x.c2964
-rw-r--r--sound/soc/codecs/wcd937x.h618
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1293
-rw-r--r--sound/soc/codecs/wcd938x.c3560
-rw-r--r--sound/soc/codecs/wcd938x.h686
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c1471
-rw-r--r--sound/soc/codecs/wcd939x.c3623
-rw-r--r--sound/soc/codecs/wcd939x.h947
-rw-r--r--sound/soc/codecs/wl1273.c523
-rw-r--r--sound/soc/codecs/wl1273.h30
-rw-r--r--sound/soc/codecs/wm0010.c299
-rw-r--r--sound/soc/codecs/wm1250-ev1.c161
-rw-r--r--sound/soc/codecs/wm2000.c157
-rw-r--r--sound/soc/codecs/wm2000.h5
-rw-r--r--sound/soc/codecs/wm2200.c346
-rw-r--r--sound/soc/codecs/wm2200.h6
-rw-r--r--sound/soc/codecs/wm5100-tables.c18
-rw-r--r--sound/soc/codecs/wm5100.c445
-rw-r--r--sound/soc/codecs/wm5100.h8
-rw-r--r--sound/soc/codecs/wm5102.c475
-rw-r--r--sound/soc/codecs/wm5102.h5
-rw-r--r--sound/soc/codecs/wm5110.c1120
-rw-r--r--sound/soc/codecs/wm5110.h5
-rw-r--r--sound/soc/codecs/wm8350.c266
-rw-r--r--sound/soc/codecs/wm8350.h10
-rw-r--r--sound/soc/codecs/wm8400.c249
-rw-r--r--sound/soc/codecs/wm8400.h7
-rw-r--r--sound/soc/codecs/wm8510.c177
-rw-r--r--sound/soc/codecs/wm8510.h5
-rw-r--r--sound/soc/codecs/wm8523.c169
-rw-r--r--sound/soc/codecs/wm8523.h7
-rw-r--r--sound/soc/codecs/wm8524.c282
-rw-r--r--sound/soc/codecs/wm8580.c279
-rw-r--r--sound/soc/codecs/wm8580.h7
-rw-r--r--sound/soc/codecs/wm8711.c141
-rw-r--r--sound/soc/codecs/wm8711.h5
-rw-r--r--sound/soc/codecs/wm8727.c32
-rw-r--r--sound/soc/codecs/wm8728.c105
-rw-r--r--sound/soc/codecs/wm8728.h5
-rw-r--r--sound/soc/codecs/wm8731-i2c.c68
-rw-r--r--sound/soc/codecs/wm8731-spi.c59
-rw-r--r--sound/soc/codecs/wm8731.c374
-rw-r--r--sound/soc/codecs/wm8731.h34
-rw-r--r--sound/soc/codecs/wm8737.c144
-rw-r--r--sound/soc/codecs/wm8737.h5
-rw-r--r--sound/soc/codecs/wm8741.c427
-rw-r--r--sound/soc/codecs/wm8741.h15
-rw-r--r--sound/soc/codecs/wm8750.c142
-rw-r--r--sound/soc/codecs/wm8750.h6
-rw-r--r--sound/soc/codecs/wm8753.c415
-rw-r--r--sound/soc/codecs/wm8753.h10
-rw-r--r--sound/soc/codecs/wm8770.c168
-rw-r--r--sound/soc/codecs/wm8770.h5
-rw-r--r--sound/soc/codecs/wm8776.c132
-rw-r--r--sound/soc/codecs/wm8776.h5
-rw-r--r--sound/soc/codecs/wm8782.c141
-rw-r--r--sound/soc/codecs/wm8804-i2c.c72
-rw-r--r--sound/soc/codecs/wm8804-spi.c52
-rw-r--r--sound/soc/codecs/wm8804.c597
-rw-r--r--sound/soc/codecs/wm8804.h13
-rw-r--r--sound/soc/codecs/wm8900.c298
-rw-r--r--sound/soc/codecs/wm8900.h5
-rw-r--r--sound/soc/codecs/wm8903.c333
-rw-r--r--sound/soc/codecs/wm8903.h10
-rw-r--r--sound/soc/codecs/wm8904.c881
-rw-r--r--sound/soc/codecs/wm8904.h6
-rw-r--r--sound/soc/codecs/wm8940.c307
-rw-r--r--sound/soc/codecs/wm8940.h8
-rw-r--r--sound/soc/codecs/wm8955.c218
-rw-r--r--sound/soc/codecs/wm8955.h5
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c365
-rw-r--r--sound/soc/codecs/wm8960.c749
-rw-r--r--sound/soc/codecs/wm8960.h8
-rw-r--r--sound/soc/codecs/wm8961.c279
-rw-r--r--sound/soc/codecs/wm8961.h5
-rw-r--r--sound/soc/codecs/wm8962.c718
-rw-r--r--sound/soc/codecs/wm8962.h13
-rw-r--r--sound/soc/codecs/wm8971.c221
-rw-r--r--sound/soc/codecs/wm8971.h7
-rw-r--r--sound/soc/codecs/wm8974.c279
-rw-r--r--sound/soc/codecs/wm8974.h5
-rw-r--r--sound/soc/codecs/wm8978.c220
-rw-r--r--sound/soc/codecs/wm8978.h7
-rw-r--r--sound/soc/codecs/wm8983.c289
-rw-r--r--sound/soc/codecs/wm8983.h5
-rw-r--r--sound/soc/codecs/wm8985.c358
-rw-r--r--sound/soc/codecs/wm8985.h43
-rw-r--r--sound/soc/codecs/wm8988.c158
-rw-r--r--sound/soc/codecs/wm8988.h6
-rw-r--r--sound/soc/codecs/wm8990.c303
-rw-r--r--sound/soc/codecs/wm8990.h7
-rw-r--r--sound/soc/codecs/wm8991.c260
-rw-r--r--sound/soc/codecs/wm8991.h6
-rw-r--r--sound/soc/codecs/wm8993.c297
-rw-r--r--sound/soc/codecs/wm8993.h1
-rw-r--r--sound/soc/codecs/wm8994.c1209
-rw-r--r--sound/soc/codecs/wm8994.h37
-rw-r--r--sound/soc/codecs/wm8995.c337
-rw-r--r--sound/soc/codecs/wm8995.h5
-rw-r--r--sound/soc/codecs/wm8996.c573
-rw-r--r--sound/soc/codecs/wm8996.h10
-rw-r--r--sound/soc/codecs/wm8997.c244
-rw-r--r--sound/soc/codecs/wm8997.h5
-rw-r--r--sound/soc/codecs/wm8998.c1437
-rw-r--r--sound/soc/codecs/wm8998.h20
-rw-r--r--sound/soc/codecs/wm9081.c280
-rw-r--r--sound/soc/codecs/wm9081.h5
-rw-r--r--sound/soc/codecs/wm9090.c155
-rw-r--r--sound/soc/codecs/wm9090.h15
-rw-r--r--sound/soc/codecs/wm9705.c266
-rw-r--r--sound/soc/codecs/wm9705.h11
-rw-r--r--sound/soc/codecs/wm9712.c338
-rw-r--r--sound/soc/codecs/wm9712.h11
-rw-r--r--sound/soc/codecs/wm9713.c507
-rw-r--r--sound/soc/codecs/wm9713.h7
-rw-r--r--sound/soc/codecs/wm_adsp.c3088
-rw-r--r--sound/soc/codecs/wm_adsp.h161
-rw-r--r--sound/soc/codecs/wm_hubs.c273
-rw-r--r--sound/soc/codecs/wm_hubs.h27
-rw-r--r--sound/soc/codecs/wmfw.h133
-rw-r--r--sound/soc/codecs/wsa881x.c1230
-rw-r--r--sound/soc/codecs/wsa883x.c1724
-rw-r--r--sound/soc/codecs/wsa884x.c2181
-rw-r--r--sound/soc/codecs/zl38060.c639
735 files changed, 387535 insertions, 28949 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index a0f265327fdf..be01f0928393 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 88pm860x-codec.c -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Author: Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * 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.
*/
#include <linux/kernel.h>
@@ -139,14 +136,14 @@ struct pm860x_priv {
unsigned int pcmclk;
unsigned int dir;
unsigned int filter;
- struct snd_soc_codec *codec;
+ 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+1];
+ unsigned char name[4][MAX_NAME_LEN];
};
/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -156,33 +153,29 @@ static const DECLARE_TLV_DB_SCALE(dpga_tlv, -9450, 150, 1);
static const DECLARE_TLV_DB_SCALE(adc_tlv, -900, 300, 0);
/* {-23, -17, -13.5, -11, -9, -6, -3, 0}dB */
-static const unsigned int mic_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 4, 7, TLV_DB_SCALE_ITEM(-900, 300, 0)
+);
/* {0, 0, 0, -6, 0, 6, 12, 18}dB */
-static const unsigned int aux_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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 unsigned int out_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+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),
-};
+ 6, 7, TLV_DB_SCALE_ITEM(-220, 220, 0)
+);
-static const unsigned int st_tlv[] = {
- TLV_DB_RANGE_HEAD(8),
+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),
@@ -190,8 +183,8 @@ static const unsigned int st_tlv[] = {
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),
-};
+ 18, 271, TLV_DB_SCALE_ITEM(-9484, 37, 0)
+);
/* Sidetone Gain = M * 2^(-5-N) */
struct st_gain {
@@ -276,15 +269,15 @@ static int snd_soc_get_volsw_2r_st(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, reg) & 0x3f;
- val[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT) >> 4) & 0xf;
- val2[0] = snd_soc_read(codec, reg2) & 0x3f;
- val2[1] = (snd_soc_read(codec, PM860X_SIDETONE_SHIFT)) & 0xf;
+ 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]))
@@ -300,7 +293,7 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned int reg = mc->reg;
unsigned int reg2 = mc->rreg;
int err;
@@ -312,18 +305,18 @@ static int snd_soc_put_volsw_2r_st(struct snd_kcontrol *kcontrol,
if (val >= ARRAY_SIZE(st_table) || val2 >= ARRAY_SIZE(st_table))
return -EINVAL;
- err = snd_soc_update_bits(codec, reg, 0x3f, st_table[val].m);
+ err = snd_soc_component_update_bits(component, reg, 0x3f, st_table[val].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0xf0,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0xf0,
st_table[val].n << 4);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, 0x3f, st_table[val2].m);
+ err = snd_soc_component_update_bits(component, reg2, 0x3f, st_table[val2].m);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, PM860X_SIDETONE_SHIFT, 0x0f,
+ err = snd_soc_component_update_bits(component, PM860X_SIDETONE_SHIFT, 0x0f,
st_table[val2].n);
return err;
}
@@ -333,15 +326,15 @@ static int snd_soc_get_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, reg) >> shift;
- val2 = snd_soc_read(codec, reg2) >> shift;
+ 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;
@@ -353,7 +346,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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;
@@ -369,11 +362,11 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
val = val << shift;
val2 = val2 << shift;
- err = snd_soc_update_bits(codec, reg, val_mask, val);
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
if (err < 0)
return err;
- err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+ err = snd_soc_component_update_bits(component, reg2, val_mask, val2);
return err;
}
@@ -386,7 +379,7 @@ static int snd_soc_put_volsw_2r_out(struct snd_kcontrol *kcontrol,
static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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
@@ -394,8 +387,8 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
* Unmute by DAC_MUTE. It should be unmuted when DAPM sequence is
* finished.
*/
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, DAC_MUTE, 0);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ 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;
}
@@ -403,41 +396,41 @@ static int pm860x_rsync_event(struct snd_soc_dapm_widget *w,
static int pm860x_dac_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
unsigned int dac = 0;
int data;
- if (!strcmp(w->name, "Left DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "Left DAC"))
dac = DAC_LEFT;
- if (!strcmp(w->name, "Right DAC"))
+ if (!snd_soc_dapm_widget_name_cmp(w, "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_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- snd_soc_update_bits(codec, PM860X_DAC_EN_2,
+ 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_update_bits(codec, PM860X_DAC_OFFSET,
+ snd_soc_component_update_bits(component, PM860X_DAC_OFFSET,
DAC_MUTE, DAC_MUTE);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ snd_soc_component_update_bits(component, PM860X_EAR_CTRL_2,
RSYNC_CHANGE, RSYNC_CHANGE);
/* update dac */
- data = snd_soc_read(codec, PM860X_DAC_EN_2);
+ data = snd_soc_component_read(component, PM860X_DAC_EN_2);
data &= ~dac;
if (!(data & (DAC_LEFT | DAC_RIGHT)))
data &= ~MODULATOR;
- snd_soc_write(codec, PM860X_DAC_EN_2, data);
+ snd_soc_component_write(component, PM860X_DAC_EN_2, data);
}
break;
}
@@ -536,10 +529,6 @@ static const struct snd_kcontrol_new pm860x_snd_controls[] = {
* DAPM Controls
*/
-/* PCM Switch / PCM Interface */
-static const struct snd_kcontrol_new pcm_switch_controls =
- SOC_DAPM_SINGLE("Switch", PM860X_ADC_EN_2, 0, 1, 0);
-
/* AUX1 Switch */
static const struct snd_kcontrol_new aux1_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_ANA_TO_ANA, 4, 1, 0);
@@ -556,17 +545,6 @@ static const struct snd_kcontrol_new lepa_switch_controls =
static const struct snd_kcontrol_new repa_switch_controls =
SOC_DAPM_SINGLE("Switch", PM860X_DAC_EN_2, 1, 1, 0);
-/* PCM Mux / Mux7 */
-static const char *aif1_text[] = {
- "PCM L", "PCM R",
-};
-
-static SOC_ENUM_SINGLE_DECL(aif1_enum,
- PM860X_PCM_IFACE_3, 6, aif1_text);
-
-static const struct snd_kcontrol_new aif1_mux =
- SOC_DAPM_ENUM("PCM Mux", aif1_enum);
-
/* I2S Mux / Mux9 */
static const char *i2s_din_text[] = {
"DIN", "DIN1",
@@ -924,15 +902,15 @@ static const struct snd_soc_dapm_route pm860x_dapm_routes[] = {
* Use MUTE_LEFT & MUTE_RIGHT to implement digital mute.
* These bits can also be used to mute.
*/
-static int pm860x_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int pm860x_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int data = 0, mask = MUTE_LEFT | MUTE_RIGHT;
if (mute)
data = mask;
- snd_soc_update_bits(codec, PM860X_DAC_OFFSET, mask, data);
- snd_soc_update_bits(codec, PM860X_EAR_CTRL_2,
+ 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;
}
@@ -941,7 +919,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf = 0, mask = 0;
/* bit size */
@@ -956,7 +934,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
mask |= PCM_INF2_18WL;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_IFACE_2, mask, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -975,7 +953,7 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_PCM_RATE, 0x0f, inf);
+ snd_soc_component_update_bits(component, PM860X_PCM_RATE, 0x0f, inf);
return 0;
}
@@ -983,23 +961,23 @@ static int pm860x_pcm_hw_params(struct snd_pcm_substream *substream,
static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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 master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN) {
inf &= ~PCM_INF2_MASTER;
ret = 0;
@@ -1016,22 +994,20 @@ static int pm860x_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
mask |= PCM_MODE_MASK;
if (ret)
return ret;
- snd_soc_update_bits(codec, PM860X_PCM_IFACE_2, mask, inf);
+ 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_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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 {
- pm860x->dir = PM860X_CLK_DIR_IN;
+ else /* Slave mode is not supported */
return -EINVAL;
- }
return 0;
}
@@ -1040,7 +1016,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned char inf;
/* bit size */
@@ -1054,7 +1030,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, PCM_INF2_18WL, inf);
/* sample rate */
switch (params_rate(params)) {
@@ -1082,7 +1058,7 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_4, 0xf, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_4, 0xf, inf);
return 0;
}
@@ -1090,21 +1066,21 @@ static int pm860x_i2s_hw_params(struct snd_pcm_substream *substream,
static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ 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 master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
if (pm860x->dir == PM860X_CLK_DIR_IN)
inf &= ~PCM_INF2_MASTER;
else
@@ -1122,14 +1098,14 @@ static int pm860x_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
mask |= PCM_MODE_MASK;
- snd_soc_update_bits(codec, PM860X_I2S_IFACE_2, mask, inf);
+ snd_soc_component_update_bits(component, PM860X_I2S_IFACE_2, mask, inf);
return 0;
}
-static int pm860x_set_bias_level(struct snd_soc_codec *codec,
+static int pm860x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
switch (level) {
@@ -1140,7 +1116,7 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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);
@@ -1156,22 +1132,23 @@ static int pm860x_set_bias_level(struct snd_soc_codec *codec,
pm860x_set_bits(pm860x->i2c, REG_MISC2, data, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
- .digital_mute = pm860x_digital_mute,
+ .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 = {
- .digital_mute = pm860x_digital_mute,
+ .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 | \
@@ -1187,16 +1164,16 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = PM860X_RATES,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .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_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_pcm_dai_ops,
}, {
@@ -1208,22 +1185,22 @@ static struct snd_soc_dai_driver pm860x_dai[] = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
- .formats = SNDRV_PCM_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .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_FORMAT_S16_LE | \
- SNDRV_PCM_FORMAT_S18_3LE,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S18_3LE,
},
.ops = &pm860x_i2s_dai_ops,
},
};
-static irqreturn_t pm860x_codec_handler(int irq, void *data)
+static irqreturn_t pm860x_component_handler(int irq, void *data)
{
struct pm860x_priv *pm860x = data;
int status, shrt, report = 0, mic_report = 0;
@@ -1237,7 +1214,7 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
#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->codec->dev));
+ trace_snd_soc_jack_irq(dev_name(pm860x->component->dev));
#endif
if ((pm860x->det.hp_det & SND_JACK_HEADPHONE)
@@ -1263,17 +1240,17 @@ static irqreturn_t pm860x_codec_handler(int irq, void *data)
snd_soc_jack_report(pm860x->det.mic_jack, SND_JACK_MICROPHONE,
SND_JACK_MICROPHONE);
- dev_dbg(pm860x->codec->dev, "headphone report:0x%x, mask:%x\n",
+ dev_dbg(pm860x->component->dev, "headphone report:0x%x, mask:%x\n",
report, mask);
- dev_dbg(pm860x->codec->dev, "microphone report:0x%x\n", mic_report);
+ dev_dbg(pm860x->component->dev, "microphone report:0x%x\n", mic_report);
return IRQ_HANDLED;
}
-int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int data;
pm860x->det.hp_jack = jack;
@@ -1297,15 +1274,15 @@ int pm860x_hs_jack_detect(struct snd_soc_codec *codec,
}
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_hs_jack_detect);
-int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
+int pm860x_mic_jack_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, int det)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
pm860x->det.mic_jack = jack;
pm860x->det.mic_det = det;
@@ -1315,24 +1292,25 @@ int pm860x_mic_jack_detect(struct snd_soc_codec *codec,
MICDET_MASK, MICDET_MASK);
/* sync status */
- pm860x_codec_handler(0, pm860x);
+ pm860x_component_handler(0, pm860x);
return 0;
}
EXPORT_SYMBOL_GPL(pm860x_mic_jack_detect);
-static int pm860x_probe(struct snd_soc_codec *codec)
+static int pm860x_probe(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i, ret;
- pm860x->codec = codec;
+ 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_codec_handler, IRQF_ONESHOT,
+ pm860x_component_handler, IRQF_ONESHOT,
pm860x->name[i], pm860x);
if (ret < 0) {
- dev_err(codec->dev, "Failed to request IRQ!\n");
+ dev_err(component->dev, "Failed to request IRQ!\n");
goto out;
}
}
@@ -1345,35 +1323,28 @@ out:
return ret;
}
-static int pm860x_remove(struct snd_soc_codec *codec)
+static void pm860x_remove(struct snd_soc_component *component)
{
- struct pm860x_priv *pm860x = snd_soc_codec_get_drvdata(codec);
+ struct pm860x_priv *pm860x = snd_soc_component_get_drvdata(component);
int i;
for (i = 3; i >= 0; i--)
free_irq(pm860x->irq[i], pm860x);
- return 0;
-}
-
-static struct regmap *pm860x_get_regmap(struct device *dev)
-{
- struct pm860x_priv *pm860x = dev_get_drvdata(dev);
-
- return pm860x->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_pm860x = {
- .probe = pm860x_probe,
- .remove = pm860x_remove,
- .set_bias_level = pm860x_set_bias_level,
- .get_regmap = pm860x_get_regmap,
-
- .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),
+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)
@@ -1402,30 +1373,24 @@ static int pm860x_codec_probe(struct platform_device *pdev)
return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
- strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
+ strscpy(pm860x->name[i], res->name, MAX_NAME_LEN);
}
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pm860x,
+ 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 codec\n");
+ dev_err(&pdev->dev, "Failed to register component\n");
return -EINVAL;
}
return ret;
}
-static int pm860x_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver pm860x_codec_driver = {
.driver = {
.name = "88pm860x-codec",
},
.probe = pm860x_codec_probe,
- .remove = pm860x_codec_remove,
};
module_platform_driver(pm860x_codec_driver);
diff --git a/sound/soc/codecs/88pm860x-codec.h b/sound/soc/codecs/88pm860x-codec.h
index f7282f4f4a79..f025146e506c 100644
--- a/sound/soc/codecs/88pm860x-codec.h
+++ b/sound/soc/codecs/88pm860x-codec.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* 88pm860x-codec.h -- 88PM860x ALSA SoC Audio Driver
*
* Copyright 2010 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
- *
- * 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 __88PM860X_H
@@ -88,9 +85,9 @@
#define PM860X_SHORT_LINEOUT (1 << 4)
#define PM860X_DET_MASK 0x1F
-extern int pm860x_hs_jack_detect(struct snd_soc_codec *, struct snd_soc_jack *,
+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_codec *, struct snd_soc_jack *,
+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
index 064e6c18e109..160c07699a8b 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -1,3 +1,4 @@
+# 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
@@ -13,183 +14,381 @@ menu "CODEC drivers"
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
depends on COMPILE_TEST
- select SND_SOC_88PM860X if MFD_88PM860X
- select SND_SOC_L3
- select SND_SOC_AB8500_CODEC if ABX500_CORE
- select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
- select SND_SOC_AD1836 if SPI_MASTER
- select SND_SOC_AD193X_SPI if SPI_MASTER
- select SND_SOC_AD193X_I2C if I2C
- select SND_SOC_AD1980 if SND_SOC_AC97_BUS
- select SND_SOC_AD73311
- select SND_SOC_ADAU1373 if I2C
- select SND_SOC_ADAU1761_I2C if I2C
- select SND_SOC_ADAU1761_SPI if SPI
- select SND_SOC_ADAU1781_I2C if I2C
- select SND_SOC_ADAU1781_SPI if SPI
- select SND_SOC_ADAV801 if SPI_MASTER
- select SND_SOC_ADAV803 if I2C
- select SND_SOC_ADAU1977_SPI if SPI_MASTER
- select SND_SOC_ADAU1977_I2C if I2C
- select SND_SOC_ADAU1701 if I2C
- select SND_SOC_ADS117X
- select SND_SOC_AK4104 if SPI_MASTER
- select SND_SOC_AK4535 if I2C
- select SND_SOC_AK4554
- select SND_SOC_AK4641 if I2C
- select SND_SOC_AK4642 if I2C
- select SND_SOC_AK4671 if I2C
- select SND_SOC_AK5386
- select SND_SOC_ALC5623 if I2C
- select SND_SOC_ALC5632 if I2C
- select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
- select SND_SOC_CS35L32 if I2C
- select SND_SOC_CS42L51_I2C if I2C
- select SND_SOC_CS42L52 if I2C && INPUT
- select SND_SOC_CS42L56 if I2C && INPUT
- select SND_SOC_CS42L73 if I2C
- select SND_SOC_CS4265 if I2C
- select SND_SOC_CS4270 if I2C
- select SND_SOC_CS4271_I2C if I2C
- select SND_SOC_CS4271_SPI if SPI_MASTER
- select SND_SOC_CS42XX8_I2C if I2C
- select SND_SOC_CX20442 if TTY
- select SND_SOC_DA7210 if I2C
- select SND_SOC_DA7213 if I2C
- select SND_SOC_DA732X if I2C
- select SND_SOC_DA9055 if I2C
- select SND_SOC_DMIC
- select SND_SOC_BT_SCO
- select SND_SOC_ES8328_SPI if SPI_MASTER
- select SND_SOC_ES8328_I2C if I2C
- select SND_SOC_ISABELLE if I2C
- select SND_SOC_JZ4740_CODEC
- select SND_SOC_LM4857 if I2C
- select SND_SOC_LM49453 if I2C
- select SND_SOC_MAX98088 if I2C
- select SND_SOC_MAX98090 if I2C
- select SND_SOC_MAX98095 if I2C
- select SND_SOC_MAX98357A
- select SND_SOC_MAX9850 if I2C
- select SND_SOC_MAX9768 if I2C
- select SND_SOC_MAX9877 if I2C
- select SND_SOC_MC13783 if MFD_MC13XXX
- select SND_SOC_ML26124 if I2C
- select SND_SOC_HDMI_CODEC
- select SND_SOC_PCM1681 if I2C
- select SND_SOC_PCM1792A if SPI_MASTER
- select SND_SOC_PCM3008
- select SND_SOC_PCM512x_I2C if I2C
- select SND_SOC_PCM512x_SPI if SPI_MASTER
- select SND_SOC_RT286 if I2C
- select SND_SOC_RT5631 if I2C
- select SND_SOC_RT5640 if I2C
- select SND_SOC_RT5645 if I2C
- select SND_SOC_RT5651 if I2C
- select SND_SOC_RT5670 if I2C
- select SND_SOC_RT5677 if I2C && SPI_MASTER
- select SND_SOC_SGTL5000 if I2C
- select SND_SOC_SI476X if MFD_SI476X_CORE
- select SND_SOC_SIRF_AUDIO_CODEC
- select SND_SOC_SN95031 if INTEL_SCU_IPC
- select SND_SOC_SPDIF
- select SND_SOC_SSM2518 if I2C
- select SND_SOC_SSM2602_SPI if SPI_MASTER
- select SND_SOC_SSM2602_I2C if I2C
- select SND_SOC_SSM4567 if I2C
- select SND_SOC_STA32X if I2C
- select SND_SOC_STA350 if I2C
- select SND_SOC_STA529 if I2C
- select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
- select SND_SOC_TAS2552 if I2C
- select SND_SOC_TAS5086 if I2C
- select SND_SOC_TFA9879 if I2C
- select SND_SOC_TLV320AIC23_I2C if I2C
- select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
- select SND_SOC_TLV320AIC26 if SPI_MASTER
- select SND_SOC_TLV320AIC31XX if I2C
- select SND_SOC_TLV320AIC32X4 if I2C
- select SND_SOC_TLV320AIC3X if I2C
- select SND_SOC_TPA6130A2 if I2C
- select SND_SOC_TLV320DAC33 if I2C
- select SND_SOC_TS3A227E if I2C
- select SND_SOC_TWL4030 if TWL4030_CORE
- select SND_SOC_TWL6040 if TWL6040_CORE
- select SND_SOC_UDA134X
- select SND_SOC_UDA1380 if I2C
- select SND_SOC_WL1273 if MFD_WL1273_CORE
- select SND_SOC_WM0010 if SPI_MASTER
- select SND_SOC_WM1250_EV1 if I2C
- select SND_SOC_WM2000 if I2C
- select SND_SOC_WM2200 if I2C
- select SND_SOC_WM5100 if I2C
- select SND_SOC_WM5102 if MFD_WM5102
- select SND_SOC_WM5110 if MFD_WM5110
- select SND_SOC_WM8350 if MFD_WM8350
- select SND_SOC_WM8400 if MFD_WM8400
- select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8523 if I2C
- select SND_SOC_WM8580 if I2C
- select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8727
- select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8737 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8741 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8770 if SPI_MASTER
- select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8782
- select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8900 if I2C
- select SND_SOC_WM8903 if I2C
- select SND_SOC_WM8904 if I2C
- select SND_SOC_WM8940 if I2C
- select SND_SOC_WM8955 if I2C
- select SND_SOC_WM8960 if I2C
- select SND_SOC_WM8961 if I2C
- select SND_SOC_WM8962 if I2C && INPUT
- select SND_SOC_WM8971 if I2C
- select SND_SOC_WM8974 if I2C
- select SND_SOC_WM8978 if I2C
- select SND_SOC_WM8983 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8990 if I2C
- select SND_SOC_WM8991 if I2C
- select SND_SOC_WM8993 if I2C
- select SND_SOC_WM8994 if MFD_WM8994
- select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
- select SND_SOC_WM8996 if I2C
- select SND_SOC_WM8997 if MFD_WM8997
- select SND_SOC_WM9081 if I2C
- select SND_SOC_WM9090 if I2C
- select SND_SOC_WM9705 if SND_SOC_AC97_BUS
- select SND_SOC_WM9712 if SND_SOC_AC97_BUS
- select SND_SOC_WM9713 if SND_SOC_AC97_BUS
- 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.
+ 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_AK4619
+ 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_AW87390
+ imply SND_SOC_AW88395
+ imply SND_SOC_AW88081
+ imply SND_SOC_AW88166
+ imply SND_SOC_AW88261
+ imply SND_SOC_AW88399
+ 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_CS40L50
+ 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_CS42L84
+ 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_CS48L32
+ imply SND_SOC_CS53L30
+ imply SND_SOC_CS530X_I2C
+ 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_ES8323
+ imply SND_SOC_ES8326
+ imply SND_SOC_ES8328_SPI
+ imply SND_SOC_ES8328_I2C
+ imply SND_SOC_ES8375
+ imply SND_SOC_ES8389
+ imply SND_SOC_ES7134
+ imply SND_SOC_ES7241
+ imply SND_SOC_FRAMER
+ imply SND_SOC_FS210X
+ 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_MT6357
+ 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_PCM1754
+ 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_PCM6240
+ imply SND_SOC_PEB2466
+ imply SND_SOC_PM4125_SDW
+ imply SND_SOC_RK3308
+ 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_RT721_SDCA_SDW
+ imply SND_SOC_RT722_SDCA_SDW
+ imply SND_SOC_RT1308_SDW
+ imply SND_SOC_RT1316_SDW
+ imply SND_SOC_RT1318
+ imply SND_SOC_RT1318_SDW
+ imply SND_SOC_RT1320_SDW
+ imply SND_SOC_RT9120
+ imply SND_SOC_RT9123
+ imply SND_SOC_RTQ9124
+ imply SND_SOC_RTQ9128
+ 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_SMA1307
+ 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_I2C
+ imply SND_SOC_TAS2783_SDW
+ 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_UDA1342
+ imply SND_SOC_UDA1380
+ imply SND_SOC_WCD9335
+ imply SND_SOC_WCD934X
+ imply SND_SOC_WCD937X_SDW
+ imply SND_SOC_WCD938X_SDW
+ imply SND_SOC_WCD939X_SDW
+ imply SND_SOC_LPASS_MACRO_COMMON
+ imply SND_SOC_LPASS_RX_MACRO
+ imply SND_SOC_LPASS_TX_MACRO
+ 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".
+ 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
@@ -198,43 +397,88 @@ config SND_SOC_WM_HUBS
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 y if SND_SOC_CS48L32=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
+ default m if SND_SOC_CS48L32=m
config SND_SOC_AB8500_CODEC
tristate
+ depends on ABX500_CORE
config SND_SOC_AC97_CODEC
- tristate
+ 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
- select REGMAP_AC97
tristate
+ depends on SND_SOC_AC97_BUS
+ select REGMAP_AC97
config SND_SOC_AD73311
tristate
-config SND_SOC_ADAU1373
+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 "Analog Devices ADAU1373 CODEC"
+ depends on I2C
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
@@ -244,18 +488,21 @@ config SND_SOC_ADAU1701
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
+ tristate "Analog Devices AU1761 CODEC - I2C"
+ depends on I2C
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
- tristate
+ tristate "Analog Devices AU1761 CODEC - SPI"
+ depends on SPI
select SND_SOC_ADAU1761
select REGMAP_SPI
@@ -265,11 +512,13 @@ config SND_SOC_ADAU1781
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
@@ -278,23 +527,57 @@ config SND_SOC_ADAU1977
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
@@ -304,14 +587,45 @@ 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_AK4619
+ tristate "AKM AK4619 CODEC"
+ depends on I2C
+
config SND_SOC_AK4641
tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
config SND_SOC_AK4642
tristate "AKM AK4642 CODEC"
@@ -319,24 +633,310 @@ config SND_SOC_AK4642
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"
+ 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
+ select CRC8
+ tristate
+
+config SND_SOC_AW88395
+ tristate "Soc Audio for awinic aw88395"
+ depends on I2C
+ 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_AW88166
+ tristate "Soc Audio for awinic aw88166"
+ depends on I2C
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88166 Smart PA.
+ The awinic AW88166 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier with sound quality
+ enhancement algorithms and speaker protection.
+
+config SND_SOC_AW88261
+ tristate "Soc Audio for awinic aw88261"
+ depends on I2C
+ 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_AW88081
+ tristate "Soc Audio for awinic aw88081/aw88083"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88081 Smart PA.
+ The awinic AW88081 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. Due to its 9uV noise
+ floor and ultra-low distortion, clean listening is guaranteed.
+
+config SND_SOC_AW87390
+ tristate "Soc Audio for awinic aw87390"
+ depends on I2C
+ select REGMAP_I2C
+ select SND_SOC_AW88395_LIB
+ help
+ The awinic aw87390 is specifically designed to improve
+ the musical output dynamic range, enhance the overall
+ sound quality, which is a new high efficiency, low
+ noise, constant large volume, 6th Smart K audio amplifier.
+
+config SND_SOC_AW88399
+ tristate "Soc Audio for awinic aw88399"
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88399 Smart PA.
+ The awinic AW88399 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier and SKTune speaker
+ protection algorithms.
+
+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_CS_AMP_LIB
+ tristate
+
+config SND_SOC_CS_AMP_LIB_TEST
+ tristate "KUnit test for Cirrus Logic cs-amp-lib" if !KUNIT_ALL_TESTS
+ depends on SND_SOC_CS_AMP_LIB && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the Cirrus Logic common
+ amplifier library.
+ For more information on KUnit and unit tests in general,
+ please refer to the KUnit documentation in
+ Documentation/dev-tools/kunit/.
+ If in doubt, say "N".
+
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
+ select SND_SOC_CS_AMP_LIB
+ 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_CS40L50
+ tristate "Cirrus Logic CS40L50 CODEC"
+ depends on MFD_CS40L50_CORE
+ help
+ This option enables support for I2S streaming to Cirrus Logic CS40L50.
+
+ CS40L50 is a haptic driver with waveform memory, an integrated
+ DSP, and closed-loop algorithms. If built as a module, it will be
+ called snd-soc-cs40l50.
+
+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
@@ -357,6 +957,24 @@ 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_CS42L84
+ tristate "Cirrus Logic CS42L84 CODEC"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+
+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
@@ -399,80 +1017,518 @@ config SND_SOC_CS42XX8_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
+
+config SND_SOC_CS48L32
+ tristate "Cirrus Logic CS48L32 audio DSP"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ help
+ Build the codec driver for the Cirrus Logic CS48L32 audio DSP.
+
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+ tristate "Cirrus Logic CS53L30 CODEC"
+ depends on I2C
+
+config SND_SOC_CS530X
+ tristate
+
+config SND_SOC_CS530X_I2C
+ tristate "Cirrus Logic CS530x ADCs (I2C)"
+ depends on I2C
+ select REGMAP
+ select REGMAP_I2C
+ select SND_SOC_CS530X
+ help
+ Enable support for Cirrus Logic CS530X ADCs
+ with I2C control.
+
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
+ 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_L3
- tristate
+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
+ tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_DA7213
- tristate
+ 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
+ tristate
+ depends on I2C
config SND_SOC_DA9055
tristate
+ depends on I2C
-config SND_SOC_BT_SCO
+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_DMIC
+config SND_SOC_ES7134
+ tristate "Everest Semi ES7134 CODEC"
+
+config SND_SOC_ES7241
+ tristate "Everest Semi ES7241 CODEC"
+
+config SND_SOC_ES83XX_DSM_COMMON
+ depends on ACPI
tristate
-config SND_SOC_HDMI_CODEC
- tristate "HDMI stub CODEC"
+config SND_SOC_ES8311
+ tristate "Everest Semi ES8311 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8316
+ tristate "Everest Semi ES8316 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8323
+ tristate "Everest Semi ES8323 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8326
+ tristate "Everest Semi ES8326 CODEC"
+ depends on I2C
config SND_SOC_ES8328
- tristate "Everest Semi ES8328 CODEC"
+ tristate
config SND_SOC_ES8328_I2C
- tristate
+ tristate "Everest Semi ES8328 CODEC (I2C)"
+ depends on I2C
select SND_SOC_ES8328
config SND_SOC_ES8328_SPI
- tristate
+ tristate "Everest Semi ES8328 CODEC (SPI)"
+ depends on SPI_MASTER
select SND_SOC_ES8328
+config SND_SOC_ES8375
+ tristate "Everest Semi ES8375 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8389
+ tristate "Everest Semi ES8389 CODEC"
+ depends on I2C
+
+config SND_SOC_FRAMER
+ tristate "Framer codec"
+ depends on GENERIC_FRAMER
+ help
+ Enable support for the framer codec.
+ The framer codec uses the generic framer infrastructure to transport
+ some audio data over an analog E1/T1/J1 line.
+ This codec allows to use some of the time slots available on the TDM
+ bus on which the framer is connected to transport the audio data.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-framer.
+
+config SND_SOC_FS_AMP_LIB
+ select CRC16
+ tristate
+
+config SND_SOC_FS210X
+ tristate 'FourSemi FS2104/5S digital audio amplifier'
+ depends on I2C
+ select GPIOLIB
+ select REGMAP_I2C
+ select SND_SOC_FS_AMP_LIB
+ help
+ Enable support for FourSemi FS2104/5S digital audio amplifier.
+ The FS2104/5S are Inductor-Less, Stereo, Closed-Loop,
+ Digital Input Class-D Power Amplifiers with Enhanced Signal Processing.
+ The amplifiers support I2C and I2S/TDM.
+
+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"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+
config SND_SOC_ISABELLE
- tristate
+ 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
+ tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec"
+ depends on I2C
config SND_SOC_MAX98090
- tristate
+ tristate "Maxim MAX98090 CODEC"
+ depends on I2C
config SND_SOC_MAX98095
- tristate
+ tristate
+ depends on I2C
config SND_SOC_MAX98357A
- tristate
+ 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_PCM1792A
- tristate "Texas Instruments PCM1792A CODEC"
+config SND_SOC_PCM1754
+ tristate "Texas Instruments PCM1754 CODEC"
+ depends on GPIOLIB
+
+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
+ 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
@@ -489,41 +1545,247 @@ config SND_SOC_PCM512x_SPI
select SND_SOC_PCM512x
select REGMAP_SPI
+config SND_SOC_PCM6240
+ tristate "Texas Instruments PCM6240 Family Audio chips based on I2C"
+ depends on I2C
+ help
+ Enable support for Texas Instruments PCM6240 Family Audio chips.
+ Note the PCM6240 driver implements a flexible and configurable
+ setting for register and filter coefficients, to one, two or
+ even multiple PCM6240 Family Audio chips.
+
+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_PM4125
+ depends on SND_SOC_PM4125_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+
+config SND_SOC_PM4125_SDW
+ tristate "PM4125 audio codec - SDW"
+ select SND_SOC_PM4125
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The PMIC PM4125 has an in-built audio codec IC used with SoCs
+ like QCM2290, and it is connected via soundwire and SPMI.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_RK3308
+ tristate "Rockchip RK3308 audio CODEC"
+ depends on ARM64 || COMPILE_TEST
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select REGMAP_MMIO
+ help
+ This is a device driver for the audio codec embedded in the
+ Rockchip RK3308 SoC.
+
+ It has 8 24-bit ADCs and 2 24-bit DACs. The maximum supported
+ sampling rate is 192 kHz.
+
+config SND_SOC_RK3328
+ tristate "Rockchip RK3328 audio CODEC"
+ depends on ARCH_ROCKCHIP || COMPILE_TEST
+ 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_RT_SDW_COMMON
+ tristate
+ default y if SND_SOC_RT721_SDCA_SDW=y
+ default m if SND_SOC_RT721_SDCA_SDW=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
+ tristate
+ depends on I2C
+
+config SND_SOC_RT1318_SDW
+ tristate "Realtek RT1318 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
+config SND_SOC_RT1320_SDW
+ tristate "Realtek RT1320 Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
+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
+ tristate "Realtek RT5640/RT5639 Codec"
+ depends on I2C
config SND_SOC_RT5645
- tristate
+ 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
@@ -531,6 +1793,148 @@ 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_RT721_SDCA_SDW
+ tristate "Realtek RT721 SDCA 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_RT9123
+ tristate "Richtek RT9123 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the I2C control mode of Richtek RT9123 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RT9123P
+ tristate "Richtek RT9123P Mono Class-D Amplifier"
+ help
+ Enable support for the HW control mode of Richtek RT9123P 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RTQ9124
+ tristate "Richtek RTQ9124 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9124 1x30W digital input automotive
+ audio amplifier with current sense and real-time load diagnostics.
+
+config SND_SOC_RTQ9128
+ tristate "Richtek RTQ9128 45W Digital Input Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9128 digital input 4-channel
+ automotive audio amplifier. It is a ultra-low output noise,
+ high-efficiency, four-channel class-D audio power amplifier
+ that can deliver over 87% power efficienty at 4x75W into 4Ohm,
+ 25V supply in automotive applications.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-rtq9128.
+
+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"
@@ -538,6 +1942,7 @@ config SND_SOC_SGTL5000
config SND_SOC_SI476X
tristate
+ depends on MFD_SI476X_CORE
config SND_SOC_SIGMADSP
tristate
@@ -551,19 +1956,53 @@ config SND_SOC_SIGMADSP_REGMAP
tristate
select SND_SOC_SIGMADSP
-config SND_SOC_SIRF_AUDIO_CODEC
- tristate "SiRF SoC internal audio codec"
- select REGMAP_MMIO
+config SND_SOC_SIMPLE_AMPLIFIER
+ tristate "Simple Audio Amplifier"
-config SND_SOC_SN95031
- tristate
+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_SMA1307
+ tristate "Iron Device SMA1307 Audio Amplifier"
+ depends on I2C
+ help
+ Enable support for Iron Device SMA1307 boosted digital speaker
+ amplifier with feedback-loop.
+ If you are using a system with an SMA1307 amplifier connected
+ via I2C, enable this option.
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
-config SND_SOC_SSM2518
+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
@@ -579,6 +2018,12 @@ config SND_SOC_SSM2602_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
@@ -594,22 +2039,140 @@ config SND_SOC_STA350
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
+ tristate
+
+config SND_SOC_TAS2781_COMLIB_I2C
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ tristate
+
+config SND_SOC_TAS2781_FMWLIB
+ select SND_SOC_TAS2781_COMLIB
+ select CRC8
+ 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_I2C
+ 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_TAS2783_SDW
+ tristate "Texas Instruments TAS2783 speaker amplifier (sdw)"
+ depends on SOUNDWIRE
+ depends on EFI
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+ select CRC32
+ help
+ Enable support for Texas Instruments TAS2783A Digital input
+ mono Class-D and DSP-inside audio power amplifiers. TAS2783
+ driver implements a flexible and configurable algorithm
+ cofficient setting, for one, two or multiple TAS2783 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
@@ -634,60 +2197,224 @@ config SND_SOC_TLV320AIC31XX
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 "Texas Instruments TLV320AIC3x CODECs"
+ 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
+ depends on GPIOLIB_LEGACY
+
+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
- select MFD_TWL4030_AUDIO
tristate
+ depends on TWL4030_CORE
+ select MFD_TWL4030_AUDIO
config SND_SOC_TWL6040
tristate
+ depends on TWL6040_CORE
-config SND_SOC_UDA134X
- tristate
+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_UDA1342
+ tristate "NXP UDA1342 CODEC"
+ depends on I2C
+ help
+ The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
+ mic inputs), stereo audio DAC, with basic audio processing.
config SND_SOC_UDA1380
- tristate
+ tristate
+ depends on I2C
+ depends on GPIOLIB_LEGACY
+
+config SND_SOC_WCD_CLASSH
+ tristate
+
+config SND_SOC_WCD_COMMON
+ 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_WL1273
+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_COMMON
+ 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_WCD937X
+ depends on SND_SOC_WCD937X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD937X_SDW
+ tristate "WCD9370/WCD9375 Codec - SDW"
+ select SND_SOC_WCD937X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9370/9375 is an audio codec IC used with SoCs
+ like SC7280 or QCM6490 chipsets, and it connected
+ via soundwire.
+ To compile this codec driver say Y or m.
+
+config SND_SOC_WCD938X
+ depends on SND_SOC_WCD938X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+ select MULTIPLEXER
+
+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_WCD939X
+ depends on SND_SOC_WCD939X_SDW
+ tristate
+ depends on SOUNDWIRE || !SOUNDWIRE
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD_CLASSH
+ select SND_SOC_WCD_COMMON
+
+config SND_SOC_WCD939X_SDW
+ tristate "WCD9390/WCD9395 Codec - SDW"
+ depends on TYPEC || !TYPEC
+ select SND_SOC_WCD939X
+ select SND_SOC_WCD_MBHC
+ select REGMAP_IRQ
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ The WCD9390/9395 is a audio codec IC Integrated in
+ Qualcomm SoCs like SM8650.
+
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"
@@ -697,8 +2424,12 @@ 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 WM8523 CODEC"
+ tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs"
depends on I2C
config SND_SOC_WM8711
@@ -713,15 +2444,26 @@ config SND_SOC_WM8728
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8731
- tristate "Wolfson Microelectronics WM8731 CODEC"
- depends on SND_SOC_I2C_AND_SPI
+ 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 WM8737 DAC"
+ tristate "Wolfson Microelectronics WM8741 DAC"
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8750
@@ -741,33 +2483,50 @@ config SND_SOC_WM8776
depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8782
- tristate
+ tristate "Wolfson Microelectronics WM8782 ADC"
config SND_SOC_WM8804
- tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver"
- depends on SND_SOC_I2C_AND_SPI
+ 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
+ tristate "Wolfson Microelectronics WM8904 CODEC"
+ depends on I2C
config SND_SOC_WM8940
- tristate
+ tristate "Wolfson Microelectronics WM8940 codec"
+ depends on I2C
config SND_SOC_WM8955
tristate
+ depends on I2C
config SND_SOC_WM8960
- tristate
+ tristate "Wolfson Microelectronics WM8960 CODEC"
+ depends on I2C
config SND_SOC_WM8961
- tristate
+ tristate "Wolfson Microelectronics WM8961 CODEC"
+ depends on I2C
config SND_SOC_WM8962
tristate "Wolfson Microelectronics WM8962 CODEC"
@@ -775,9 +2534,11 @@ config SND_SOC_WM8962
config SND_SOC_WM8971
tristate
+ depends on I2C
config SND_SOC_WM8974
- tristate
+ tristate "Wolfson Microelectronics WM8974 codec"
+ depends on I2C
config SND_SOC_WM8978
tristate "Wolfson Microelectronics WM8978 codec"
@@ -785,67 +2546,241 @@ config SND_SOC_WM8978
config SND_SOC_WM8983
tristate
+ depends on SND_SOC_I2C_AND_SPI
config SND_SOC_WM8985
- tristate
+ 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
+ depends on MFD_WM8994
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 "Wolfson Microelectronics WM8998 codec driver"
+ 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
+ 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
+ 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
+ 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_MT6357
+ tristate "MediaTek MT6357 Codec"
+ help
+ Enable support for the platform which uses MT6357 as
+ external codec device.
+
+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_NTPFW
+ tristate
+
+config SND_SOC_NTP8918
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8918 amplifier"
+ depends on I2C
+
+config SND_SOC_NTP8835
+ select SND_SOC_NTPFW
+ tristate "NeoFidelity NTP8835 and NTP8835C amplifiers"
+ 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
+ select SND_SOC_LPASS_MACRO_COMMON
+ 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
index 69b8666d187a..bd95a7c911d5 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,182 +1,433 @@
-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-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-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-ak4535-objs := ak4535.o
-snd-soc-ak4554-objs := ak4554.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-arizona-objs := arizona.o
-snd-soc-cq93vc-objs := cq93vc.o
-snd-soc-cs35l32-objs := cs35l32.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-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-cx20442-objs := cx20442.o
-snd-soc-da7210-objs := da7210.o
-snd-soc-da7213-objs := da7213.o
-snd-soc-da732x-objs := da732x.o
-snd-soc-da9055-objs := da9055.o
-snd-soc-bt-sco-objs := bt-sco.o
-snd-soc-dmic-objs := dmic.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-isabelle-objs := isabelle.o
-snd-soc-jz4740-codec-objs := jz4740.o
-snd-soc-l3-objs := l3.o
-snd-soc-lm4857-objs := lm4857.o
-snd-soc-lm49453-objs := lm49453.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-max9850-objs := max9850.o
-snd-soc-mc13783-objs := mc13783.o
-snd-soc-ml26124-objs := ml26124.o
-snd-soc-hdmi-codec-objs := hdmi.o
-snd-soc-pcm1681-objs := pcm1681.o
-snd-soc-pcm1792a-codec-objs := pcm1792a.o
-snd-soc-pcm3008-objs := pcm3008.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-rl6231-objs := rl6231.o
-snd-soc-rt286-objs := rt286.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-rt5670-objs := rt5670.o
-snd-soc-rt5677-objs := rt5677.o
-snd-soc-rt5677-spi-objs := rt5677-spi.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-sirf-audio-codec-objs := sirf-audio-codec.o
-snd-soc-sn95031-objs := sn95031.o
-snd-soc-spdif-tx-objs := spdif_transmitter.o
-snd-soc-spdif-rx-objs := spdif_receiver.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-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-tas5086-objs := tas5086.o
-snd-soc-tfa9879-objs := tfa9879.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
-snd-soc-tlv320aic3x-objs := tlv320aic3x.o
-snd-soc-tlv320dac33-objs := tlv320dac33.o
-snd-soc-ts3a227e-objs := ts3a227e.o
-snd-soc-twl4030-objs := twl4030.o
-snd-soc-twl6040-objs := twl6040.o
-snd-soc-uda134x-objs := uda134x.o
-snd-soc-uda1380-objs := uda1380.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-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-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-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-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
-
+# SPDX-License-Identifier: GPL-2.0
+snd-soc-88pm860x-y := 88pm860x-codec.o
+snd-soc-ab8500-codec-y := ab8500-codec.o
+snd-soc-ac97-y := ac97.o
+snd-soc-ad1836-y := ad1836.o
+snd-soc-ad193x-y := ad193x.o
+snd-soc-ad193x-spi-y := ad193x-spi.o
+snd-soc-ad193x-i2c-y := ad193x-i2c.o
+snd-soc-ad1980-y := ad1980.o
+snd-soc-ad73311-y := ad73311.o
+snd-soc-adau-utils-y := adau-utils.o
+snd-soc-adau1372-y := adau1372.o
+snd-soc-adau1372-i2c-y := adau1372-i2c.o
+snd-soc-adau1372-spi-y := adau1372-spi.o
+snd-soc-adau1373-y := adau1373.o
+snd-soc-adau1701-y := adau1701.o
+snd-soc-adau17x1-y := adau17x1.o
+snd-soc-adau1761-y := adau1761.o
+snd-soc-adau1761-i2c-y := adau1761-i2c.o
+snd-soc-adau1761-spi-y := adau1761-spi.o
+snd-soc-adau1781-y := adau1781.o
+snd-soc-adau1781-i2c-y := adau1781-i2c.o
+snd-soc-adau1781-spi-y := adau1781-spi.o
+snd-soc-adau1977-y := adau1977.o
+snd-soc-adau1977-spi-y := adau1977-spi.o
+snd-soc-adau1977-i2c-y := adau1977-i2c.o
+snd-soc-adau7002-y := adau7002.o
+snd-soc-adau7118-y := adau7118.o
+snd-soc-adau7118-i2c-y := adau7118-i2c.o
+snd-soc-adau7118-hw-y := adau7118-hw.o
+snd-soc-adav80x-y := adav80x.o
+snd-soc-adav801-y := adav801.o
+snd-soc-adav803-y := adav803.o
+snd-soc-ads117x-y := ads117x.o
+snd-soc-ak4104-y := ak4104.o
+snd-soc-ak4118-y := ak4118.o
+snd-soc-ak4375-y := ak4375.o
+snd-soc-ak4458-y := ak4458.o
+snd-soc-ak4535-y := ak4535.o
+snd-soc-ak4554-y := ak4554.o
+snd-soc-ak4613-y := ak4613.o
+snd-soc-ak4619-y := ak4619.o
+snd-soc-ak4641-y := ak4641.o
+snd-soc-ak4642-y := ak4642.o
+snd-soc-ak4671-y := ak4671.o
+snd-soc-ak5386-y := ak5386.o
+snd-soc-ak5558-y := ak5558.o
+snd-soc-arizona-y := arizona.o arizona-jack.o
+snd-soc-audio-iio-aux-y := audio-iio-aux.o
+snd-soc-aw8738-y := aw8738.o
+snd-soc-aw87390-y := aw87390.o
+snd-soc-aw88081-y := aw88081.o
+snd-soc-aw88395-lib-y := aw88395/aw88395_lib.o
+snd-soc-aw88395-y := aw88395/aw88395.o \
+ aw88395/aw88395_device.o
+snd-soc-aw88166-y := aw88166.o
+snd-soc-aw88261-y := aw88261.o
+snd-soc-aw88399-y := aw88399.o
+snd-soc-bd28623-y := bd28623.o
+snd-soc-bt-sco-y := bt-sco.o
+snd-soc-chv3-codec-y := chv3-codec.o
+snd-soc-cpcap-y := cpcap.o
+snd-soc-cq93vc-y := cq93vc.o
+snd-soc-cros-ec-codec-y := cros_ec_codec.o
+snd-soc-cs-amp-lib-y := cs-amp-lib.o
+snd-soc-cs-amp-lib-test-y := cs-amp-lib-test.o
+snd-soc-cs35l32-y := cs35l32.o
+snd-soc-cs35l33-y := cs35l33.o
+snd-soc-cs35l34-y := cs35l34.o
+snd-soc-cs35l35-y := cs35l35.o
+snd-soc-cs35l36-y := cs35l36.o
+snd-soc-cs35l41-lib-y := cs35l41-lib.o
+snd-soc-cs35l41-y := cs35l41.o
+snd-soc-cs35l41-spi-y := cs35l41-spi.o
+snd-soc-cs35l41-i2c-y := cs35l41-i2c.o
+snd-soc-cs35l45-y := cs35l45.o cs35l45-tables.o
+snd-soc-cs35l45-spi-y := cs35l45-spi.o
+snd-soc-cs35l45-i2c-y := cs35l45-i2c.o
+snd-soc-cs35l56-y := cs35l56.o
+snd-soc-cs35l56-shared-y := cs35l56-shared.o
+snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
+snd-soc-cs35l56-spi-y := cs35l56-spi.o
+snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
+snd-soc-cs40l50-y := cs40l50-codec.o
+snd-soc-cs42l42-y := cs42l42.o
+snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
+snd-soc-cs42l42-sdw-y := cs42l42-sdw.o
+snd-soc-cs42l43-y := cs42l43.o cs42l43-jack.o
+snd-soc-cs42l43-sdw-y := cs42l43-sdw.o
+snd-soc-cs42l51-y := cs42l51.o
+snd-soc-cs42l51-i2c-y := cs42l51-i2c.o
+snd-soc-cs42l52-y := cs42l52.o
+snd-soc-cs42l56-y := cs42l56.o
+snd-soc-cs42l73-y := cs42l73.o
+snd-soc-cs42l83-i2c-y := cs42l83-i2c.o
+snd-soc-cs42l84-y := cs42l84.o
+snd-soc-cs4234-y := cs4234.o
+snd-soc-cs4265-y := cs4265.o
+snd-soc-cs4270-y := cs4270.o
+snd-soc-cs4271-y := cs4271.o
+snd-soc-cs4271-i2c-y := cs4271-i2c.o
+snd-soc-cs4271-spi-y := cs4271-spi.o
+snd-soc-cs42xx8-y := cs42xx8.o
+snd-soc-cs42xx8-i2c-y := cs42xx8-i2c.o
+snd-soc-cs43130-y := cs43130.o
+snd-soc-cs4341-y := cs4341.o
+snd-soc-cs4349-y := cs4349.o
+snd-soc-cs47l15-y := cs47l15.o
+snd-soc-cs47l24-y := cs47l24.o
+snd-soc-cs47l35-y := cs47l35.o
+snd-soc-cs47l85-y := cs47l85.o
+snd-soc-cs47l90-y := cs47l90.o
+snd-soc-cs47l92-y := cs47l92.o
+snd-soc-cs48l32-y := cs48l32.o cs48l32-tables.o
+snd-soc-cs53l30-y := cs53l30.o
+snd-soc-cs530x-y := cs530x.o
+snd-soc-cs530x-i2c-y := cs530x-i2c.o
+snd-soc-cx20442-y := cx20442.o
+snd-soc-cx2072x-y := cx2072x.o
+snd-soc-da7210-y := da7210.o
+snd-soc-da7213-y := da7213.o
+snd-soc-da7218-y := da7218.o
+snd-soc-da7219-y := da7219.o da7219-aad.o
+snd-soc-da732x-y := da732x.o
+snd-soc-da9055-y := da9055.o
+snd-soc-dmic-y := dmic.o
+snd-soc-es7134-y := es7134.o
+snd-soc-es7241-y := es7241.o
+snd-soc-es83xx-dsm-common-y := es83xx-dsm-common.o
+snd-soc-es8311-y := es8311.o
+snd-soc-es8316-y := es8316.o
+snd-soc-es8323-y := es8323.o
+snd-soc-es8326-y := es8326.o
+snd-soc-es8328-y := es8328.o
+snd-soc-es8328-i2c-y := es8328-i2c.o
+snd-soc-es8328-spi-y := es8328-spi.o
+snd-soc-es8375-y := es8375.o
+snd-soc-es8389-y := es8389.o
+snd-soc-framer-y := framer-codec.o
+snd-soc-fs-amp-lib-y := fs-amp-lib.o
+snd-soc-fs210x-y := fs210x.o
+snd-soc-gtm601-y := gtm601.o
+snd-soc-hdac-hdmi-y := hdac_hdmi.o
+snd-soc-hdac-hda-y := hdac_hda.o
+snd-soc-hda-codec-y := hda.o hda-dai.o
+snd-soc-ics43432-y := ics43432.o
+snd-soc-idt821034-y := idt821034.o
+snd-soc-inno-rk3036-y := inno_rk3036.o
+snd-soc-isabelle-y := isabelle.o
+snd-soc-jz4740-codec-y := jz4740.o
+snd-soc-jz4725b-codec-y := jz4725b.o
+snd-soc-jz4760-codec-y := jz4760.o
+snd-soc-jz4770-codec-y := jz4770.o
+snd-soc-lm4857-y := lm4857.o
+snd-soc-lm49453-y := lm49453.o
+snd-soc-lochnagar-sc-y := lochnagar-sc.o
+snd-soc-lpass-macro-common-y := lpass-macro-common.o
+snd-soc-lpass-rx-macro-y := lpass-rx-macro.o
+snd-soc-lpass-tx-macro-y := lpass-tx-macro.o
+snd-soc-lpass-wsa-macro-y := lpass-wsa-macro.o
+snd-soc-lpass-va-macro-y := lpass-va-macro.o
+snd-soc-madera-y := madera.o
+snd-soc-max9759-y := max9759.o
+snd-soc-max9768-y := max9768.o
+snd-soc-max98088-y := max98088.o
+snd-soc-max98090-y := max98090.o
+snd-soc-max98095-y := max98095.o
+snd-soc-max98357a-y := max98357a.o
+snd-soc-max98371-y := max98371.o
+snd-soc-max9867-y := max9867.o
+snd-soc-max98925-y := max98925.o
+snd-soc-max98926-y := max98926.o
+snd-soc-max98927-y := max98927.o
+snd-soc-max98520-y := max98520.o
+snd-soc-max98363-y := max98363.o
+snd-soc-max98373-y := max98373.o
+snd-soc-max98373-i2c-y := max98373-i2c.o
+snd-soc-max98373-sdw-y := max98373-sdw.o
+snd-soc-max98388-y := max98388.o
+snd-soc-max98390-y := max98390.o
+snd-soc-max98396-y := max98396.o
+snd-soc-max9850-y := max9850.o
+snd-soc-max9860-y := max9860.o
+snd-soc-mc13783-y := mc13783.o
+snd-soc-ml26124-y := ml26124.o
+snd-soc-msm8916-analog-y := msm8916-wcd-analog.o
+snd-soc-msm8916-digital-y := msm8916-wcd-digital.o
+snd-soc-mt6351-y := mt6351.o
+snd-soc-mt6357-y := mt6357.o
+snd-soc-mt6358-y := mt6358.o
+snd-soc-mt6359-y := mt6359.o
+snd-soc-mt6359-accdet-y := mt6359-accdet.o
+snd-soc-mt6660-y := mt6660.o
+snd-soc-nau8315-y := nau8315.o
+snd-soc-nau8540-y := nau8540.o
+snd-soc-nau8810-y := nau8810.o
+snd-soc-nau8821-y := nau8821.o
+snd-soc-nau8822-y := nau8822.o
+snd-soc-nau8824-y := nau8824.o
+snd-soc-nau8825-y := nau8825.o
+snd-soc-ntp8835-y := ntp8835.o
+snd-soc-ntp8918-y := ntp8918.o
+snd-soc-ntpfw-y := ntpfw.o
+snd-soc-hdmi-codec-y := hdmi-codec.o
+snd-soc-pcm1681-y := pcm1681.o
+snd-soc-pcm1754-y := pcm1754.o
+snd-soc-pcm1789-codec-y := pcm1789.o
+snd-soc-pcm1789-i2c-y := pcm1789-i2c.o
+snd-soc-pcm179x-codec-y := pcm179x.o
+snd-soc-pcm179x-i2c-y := pcm179x-i2c.o
+snd-soc-pcm179x-spi-y := pcm179x-spi.o
+snd-soc-pcm186x-y := pcm186x.o
+snd-soc-pcm186x-i2c-y := pcm186x-i2c.o
+snd-soc-pcm186x-spi-y := pcm186x-spi.o
+snd-soc-pcm3008-y := pcm3008.o
+snd-soc-pcm3060-y := pcm3060.o
+snd-soc-pcm3060-i2c-y := pcm3060-i2c.o
+snd-soc-pcm3060-spi-y := pcm3060-spi.o
+snd-soc-pcm3168a-y := pcm3168a.o
+snd-soc-pcm3168a-i2c-y := pcm3168a-i2c.o
+snd-soc-pcm3168a-spi-y := pcm3168a-spi.o
+snd-soc-pcm5102a-y := pcm5102a.o
+snd-soc-pcm512x-y := pcm512x.o
+snd-soc-pcm512x-i2c-y := pcm512x-i2c.o
+snd-soc-pcm512x-spi-y := pcm512x-spi.o
+snd-soc-pcm6240-y := pcm6240.o
+snd-soc-peb2466-y := peb2466.o
+snd-soc-pm4125-y := pm4125.o
+snd-soc-pm4125-sdw-y := pm4125-sdw.o
+snd-soc-rk3308-y := rk3308_codec.o
+snd-soc-rk3328-y := rk3328_codec.o
+snd-soc-rk817-y := rk817_codec.o
+snd-soc-rl6231-y := rl6231.o
+snd-soc-rt-sdw-common-y := rt-sdw-common.o
+snd-soc-rl6347a-y := rl6347a.o
+snd-soc-rt1011-y := rt1011.o
+snd-soc-rt1015-y := rt1015.o
+snd-soc-rt1015p-y := rt1015p.o
+snd-soc-rt1016-y := rt1016.o
+snd-soc-rt1017-sdca-y := rt1017-sdca-sdw.o
+snd-soc-rt1019-y := rt1019.o
+snd-soc-rt1305-y := rt1305.o
+snd-soc-rt1308-y := rt1308.o
+snd-soc-rt1308-sdw-y := rt1308-sdw.o
+snd-soc-rt1316-sdw-y := rt1316-sdw.o
+snd-soc-rt1318-y := rt1318.o
+snd-soc-rt1318-sdw-y := rt1318-sdw.o
+snd-soc-rt1320-sdw-y := rt1320-sdw.o
+snd-soc-rt274-y := rt274.o
+snd-soc-rt286-y := rt286.o
+snd-soc-rt298-y := rt298.o
+snd-soc-rt5514-y := rt5514.o
+snd-soc-rt5514-spi-y := rt5514-spi.o
+snd-soc-rt5616-y := rt5616.o
+snd-soc-rt5631-y := rt5631.o
+snd-soc-rt5640-y := rt5640.o
+snd-soc-rt5645-y := rt5645.o
+snd-soc-rt5651-y := rt5651.o
+snd-soc-rt5659-y := rt5659.o
+snd-soc-rt5660-y := rt5660.o
+snd-soc-rt5663-y := rt5663.o
+snd-soc-rt5665-y := rt5665.o
+snd-soc-rt5668-y := rt5668.o
+snd-soc-rt5670-y := rt5670.o
+snd-soc-rt5677-y := rt5677.o
+snd-soc-rt5677-spi-y := rt5677-spi.o
+snd-soc-rt5682-y := rt5682.o
+snd-soc-rt5682-sdw-y := rt5682-sdw.o
+snd-soc-rt5682-i2c-y := rt5682-i2c.o
+snd-soc-rt5682s-y := rt5682s.o
+snd-soc-rt700-y := rt700.o rt700-sdw.o
+snd-soc-rt711-y := rt711.o rt711-sdw.o
+snd-soc-rt711-sdca-y := rt711-sdca.o rt711-sdca-sdw.o
+snd-soc-rt712-sdca-y := rt712-sdca.o rt712-sdca-sdw.o
+snd-soc-rt712-sdca-dmic-y := rt712-sdca-dmic.o
+snd-soc-rt715-y := rt715.o rt715-sdw.o
+snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o
+snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o
+snd-soc-rt9120-y := rt9120.o
+snd-soc-rt9123-y := rt9123.o
+snd-soc-rt9123p-y := rt9123p.o
+snd-soc-rtq9124-y := rtq9124.o
+snd-soc-rtq9128-y := rtq9128.o
+snd-soc-sdw-mockup-y := sdw-mockup.o
+snd-soc-sgtl5000-y := sgtl5000.o
+snd-soc-alc5623-y := alc5623.o
+snd-soc-alc5632-y := alc5632.o
+snd-soc-sigmadsp-y := sigmadsp.o
+snd-soc-sigmadsp-i2c-y := sigmadsp-i2c.o
+snd-soc-sigmadsp-regmap-y := sigmadsp-regmap.o
+snd-soc-si476x-y := si476x.o
+snd-soc-sma1303-y := sma1303.o
+snd-soc-sma1307-y := sma1307.o
+snd-soc-spdif-tx-y := spdif_transmitter.o
+snd-soc-spdif-rx-y := spdif_receiver.o
+snd-soc-src4xxx-y := src4xxx.o
+snd-soc-src4xxx-i2c-y := src4xxx-i2c.o
+snd-soc-ssm2305-y := ssm2305.o
+snd-soc-ssm2518-y := ssm2518.o
+snd-soc-ssm2602-y := ssm2602.o
+snd-soc-ssm2602-spi-y := ssm2602-spi.o
+snd-soc-ssm2602-i2c-y := ssm2602-i2c.o
+snd-soc-ssm3515-y := ssm3515.o
+snd-soc-ssm4567-y := ssm4567.o
+snd-soc-sta32x-y := sta32x.o
+snd-soc-sta350-y := sta350.o
+snd-soc-sta529-y := sta529.o
+snd-soc-stac9766-y := stac9766.o
+snd-soc-sti-sas-y := sti-sas.o
+snd-soc-tas5086-y := tas5086.o
+snd-soc-tas571x-y := tas571x.o
+snd-soc-tas5720-y := tas5720.o
+snd-soc-tas5805m-y := tas5805m.o
+snd-soc-tas6424-y := tas6424.o
+snd-soc-tda7419-y := tda7419.o
+snd-soc-tas2770-y := tas2770.o
+snd-soc-tas2781-comlib-y := tas2781-comlib.o
+snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
+snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
+snd-soc-tas2781-i2c-y := tas2781-i2c.o
+snd-soc-tas2783-sdw-y := tas2783-sdw.o
+snd-soc-tfa9879-y := tfa9879.o
+snd-soc-tfa989x-y := tfa989x.o
+snd-soc-tlv320adc3xxx-y := tlv320adc3xxx.o
+snd-soc-tlv320aic23-y := tlv320aic23.o
+snd-soc-tlv320aic23-i2c-y := tlv320aic23-i2c.o
+snd-soc-tlv320aic23-spi-y := tlv320aic23-spi.o
+snd-soc-tlv320aic26-y := tlv320aic26.o
+snd-soc-tlv320aic31xx-y := tlv320aic31xx.o
+snd-soc-tlv320aic32x4-y := tlv320aic32x4.o tlv320aic32x4-clk.o
+snd-soc-tlv320aic32x4-i2c-y := tlv320aic32x4-i2c.o
+snd-soc-tlv320aic32x4-spi-y := tlv320aic32x4-spi.o
+snd-soc-tlv320aic3x-y := tlv320aic3x.o
+snd-soc-tlv320aic3x-i2c-y := tlv320aic3x-i2c.o
+snd-soc-tlv320aic3x-spi-y := tlv320aic3x-spi.o
+snd-soc-tlv320dac33-y := tlv320dac33.o
+snd-soc-tlv320adcx140-y := tlv320adcx140.o
+snd-soc-tscs42xx-y := tscs42xx.o
+snd-soc-tscs454-y := tscs454.o
+snd-soc-ts3a227e-y := ts3a227e.o
+snd-soc-twl4030-y := twl4030.o
+snd-soc-twl6040-y := twl6040.o
+snd-soc-uda1334-y := uda1334.o
+snd-soc-uda1342-y := uda1342.o
+snd-soc-uda1380-y := uda1380.o
+snd-soc-wcd-classh-y := wcd-clsh-v2.o
+snd-soc-wcd-common-y := wcd-common.o
+snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
+snd-soc-wcd9335-y := wcd9335.o
+snd-soc-wcd934x-y := wcd934x.o
+snd-soc-wcd937x-y := wcd937x.o
+snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
+snd-soc-wcd938x-y := wcd938x.o
+snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
+snd-soc-wcd939x-y := wcd939x.o
+snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
+snd-soc-wm-adsp-y := wm_adsp.o
+snd-soc-wm0010-y := wm0010.o
+snd-soc-wm1250-ev1-y := wm1250-ev1.o
+snd-soc-wm2000-y := wm2000.o
+snd-soc-wm2200-y := wm2200.o
+snd-soc-wm5100-y := wm5100.o wm5100-tables.o
+snd-soc-wm5102-y := wm5102.o
+snd-soc-wm5110-y := wm5110.o
+snd-soc-wm8350-y := wm8350.o
+snd-soc-wm8400-y := wm8400.o
+snd-soc-wm8510-y := wm8510.o
+snd-soc-wm8523-y := wm8523.o
+snd-soc-wm8524-y := wm8524.o
+snd-soc-wm8580-y := wm8580.o
+snd-soc-wm8711-y := wm8711.o
+snd-soc-wm8727-y := wm8727.o
+snd-soc-wm8728-y := wm8728.o
+snd-soc-wm8731-y := wm8731.o
+snd-soc-wm8731-i2c-y := wm8731-i2c.o
+snd-soc-wm8731-spi-y := wm8731-spi.o
+snd-soc-wm8737-y := wm8737.o
+snd-soc-wm8741-y := wm8741.o
+snd-soc-wm8750-y := wm8750.o
+snd-soc-wm8753-y := wm8753.o
+snd-soc-wm8770-y := wm8770.o
+snd-soc-wm8776-y := wm8776.o
+snd-soc-wm8782-y := wm8782.o
+snd-soc-wm8804-y := wm8804.o
+snd-soc-wm8804-i2c-y := wm8804-i2c.o
+snd-soc-wm8804-spi-y := wm8804-spi.o
+snd-soc-wm8900-y := wm8900.o
+snd-soc-wm8903-y := wm8903.o
+snd-soc-wm8904-y := wm8904.o
+snd-soc-wm8996-y := wm8996.o
+snd-soc-wm8940-y := wm8940.o
+snd-soc-wm8955-y := wm8955.o
+snd-soc-wm8960-y := wm8960.o
+snd-soc-wm8961-y := wm8961.o
+snd-soc-wm8962-y := wm8962.o
+snd-soc-wm8971-y := wm8971.o
+snd-soc-wm8974-y := wm8974.o
+snd-soc-wm8978-y := wm8978.o
+snd-soc-wm8983-y := wm8983.o
+snd-soc-wm8985-y := wm8985.o
+snd-soc-wm8988-y := wm8988.o
+snd-soc-wm8990-y := wm8990.o
+snd-soc-wm8991-y := wm8991.o
+snd-soc-wm8993-y := wm8993.o
+snd-soc-wm8994-y := wm8994.o wm8958-dsp2.o
+snd-soc-wm8995-y := wm8995.o
+snd-soc-wm8997-y := wm8997.o
+snd-soc-wm8998-y := wm8998.o
+snd-soc-wm9081-y := wm9081.o
+snd-soc-wm9090-y := wm9090.o
+snd-soc-wm9705-y := wm9705.o
+snd-soc-wm9712-y := wm9712.o
+snd-soc-wm9713-y := wm9713.o
+snd-soc-wm-hubs-y := wm_hubs.o
+snd-soc-wsa881x-y := wsa881x.o
+snd-soc-wsa883x-y := wsa883x.o
+snd-soc-wsa884x-y := wsa884x.o
+snd-soc-zl38060-y := zl38060.o
# Amp
-snd-soc-max9877-objs := max9877.o
-snd-soc-tpa6130a2-objs := tpa6130a2.o
-snd-soc-tas2552-objs := tas2552.o
+snd-soc-max9877-y := max9877.o
+snd-soc-max98504-y := max98504.o
+snd-soc-simple-amplifier-y := simple-amplifier.o
+snd-soc-tpa6130a2-y := tpa6130a2.o
+snd-soc-tas2552-y := tas2552.o
+snd-soc-tas2562-y := tas2562.o
+snd-soc-tas2764-y := tas2764.o
+snd-soc-tas2780-y := tas2780.o
+# Mux
+snd-soc-simple-mux-y := simple-mux.o
obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o
obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o
@@ -187,6 +438,10 @@ 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
@@ -199,27 +454,78 @@ 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_AK4619) += snd-soc-ak4619.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_AW87390) += snd-soc-aw87390.o
+obj-$(CONFIG_SND_SOC_AW88081) += snd-soc-aw88081.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_AW88166) +=snd-soc-aw88166.o
+obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o
+obj-$(CONFIG_SND_SOC_AW88399) += snd-soc-aw88399.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_CS_AMP_LIB) += snd-soc-cs-amp-lib.o
+obj-$(CONFIG_SND_SOC_CS_AMP_LIB_TEST) += snd-soc-cs-amp-lib-test.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_CS40L50) += snd-soc-cs40l50.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_CS42L84) += snd-soc-cs42l84.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
@@ -227,78 +533,269 @@ 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_CS48L32) += snd-soc-cs48l32.o
+obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
+obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o
+obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.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_BT_SCO) += snd-soc-bt-sco.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_ES83XX_DSM_COMMON) += snd-soc-es83xx-dsm-common.o
+obj-$(CONFIG_SND_SOC_ES8311) += snd-soc-es8311.o
+obj-$(CONFIG_SND_SOC_ES8316) += snd-soc-es8316.o
+obj-$(CONFIG_SND_SOC_ES8323) += snd-soc-es8323.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_ES8375) += snd-soc-es8375.o
+obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o
+obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
+obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
+obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.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_L3) += snd-soc-l3.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_HDMI_CODEC) += snd-soc-hdmi-codec.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_MT6357) += snd-soc-mt6357.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_NTP8835) += snd-soc-ntp8835.o
+obj-$(CONFIG_SND_SOC_NTP8918) += snd-soc-ntp8918.o
+obj-$(CONFIG_SND_SOC_NTPFW) += snd-soc-ntpfw.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_PCM1792A) += snd-soc-pcm1792a-codec.o
+obj-$(CONFIG_SND_SOC_PCM1754) += snd-soc-pcm1754.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_PCM6240) += snd-soc-pcm6240.o
+obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o
+obj-$(CONFIG_SND_SOC_PM4125_SDW) += snd-soc-pm4125-sdw.o
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125.o
+ifdef CONFIG_SND_SOC_PM4125_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_PM4125) += snd-soc-pm4125-sdw.o
+endif
+obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.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_RT_SDW_COMMON) += snd-soc-rt-sdw-common.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) += snd-soc-rt1318.o
+obj-$(CONFIG_SND_SOC_RT1318_SDW) += snd-soc-rt1318-sdw.o
+obj-$(CONFIG_SND_SOC_RT1320_SDW) += snd-soc-rt1320-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_RT721_SDCA_SDW) += snd-soc-rt721-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_RT9123) += snd-soc-rt9123.o
+obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o
+obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o
+obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.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_SN95031) +=snd-soc-sn95031.o
+obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
+obj-$(CONFIG_SND_SOC_SMA1307) += snd-soc-sma1307.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_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.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_TAS2783_SDW) += snd-soc-tas2783-sdw.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_UDA134X) += snd-soc-uda134x.o
+obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
+obj-$(CONFIG_SND_SOC_UDA1342) += snd-soc-uda1342.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
-obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_CLASSH) += snd-soc-wcd-classh.o
+obj-$(CONFIG_SND_SOC_WCD_COMMON) += snd-soc-wcd-common.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_WCD937X) += snd-soc-wcd937x.o
+ifdef CONFIG_SND_SOC_WCD937X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD937X) += snd-soc-wcd937x-sdw.o
+endif
+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_WCD939X) += snd-soc-wcd939x.o
+ifdef CONFIG_SND_SOC_WCD939X_SDW
+# avoid link failure by forcing sdw code built-in when needed
+obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
+endif
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
@@ -310,11 +807,14 @@ 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
@@ -323,6 +823,8 @@ 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
@@ -344,6 +846,7 @@ 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
@@ -351,7 +854,21 @@ 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
index 7895689588da..04b5e1d5a653 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -11,12 +12,6 @@
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* Jarmo K. Kuronen <jarmo.kuronen@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * 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.
*/
#include <linux/kernel.h>
@@ -116,13 +111,6 @@ enum amic_idx {
AMIC_IDX_2
};
-struct ab8500_codec_drvdata_dbg {
- struct regulator *vaud;
- struct regulator *vamic1;
- struct regulator *vamic2;
- struct regulator *vdmic;
-};
-
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
@@ -1037,86 +1025,86 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
};
/* ANC FIR-coefficients configuration sequence */
-static void anc_fir(struct snd_soc_codec *codec,
+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_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
- snd_soc_write(codec, AB8500_ANCCONF5, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF6, val & 0xff);
+ 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_update_bits(codec, AB8500_ANCCONF1,
+ 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_codec *codec, unsigned int bnk,
+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_update_bits(codec, AB8500_ANCCONF1,
+ 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);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ 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);
+ usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY*2);
} else {
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
}
} else if (par > 3) {
- snd_soc_write(codec, AB8500_ANCCONF7, 0);
- snd_soc_write(codec, AB8500_ANCCONF8, val >> 16 & 0xff);
+ snd_soc_component_write(component, AB8500_ANCCONF7, 0);
+ snd_soc_component_write(component, AB8500_ANCCONF8, val >> 16 & 0xff);
}
- snd_soc_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_ANCCONF8, val & 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_update_bits(codec, AB8500_ANCCONF1,
+ 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_codec *codec,
+static void anc_configure(struct snd_soc_component *component,
bool apply_fir, bool apply_iir)
{
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(component->dev);
unsigned int bnk, par, val;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
if (apply_fir)
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ snd_soc_component_update_bits(component, AB8500_ANCCONF1,
BIT(AB8500_ANCCONF1_ENANC), 0);
- snd_soc_update_bits(codec, AB8500_ANCCONF1,
+ 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_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_fir_values[par]);
- anc_fir(codec, bnk, par, val);
+ 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_read(codec,
+ val = snd_soc_component_read(component,
drvdata->anc_iir_values[par]);
- anc_iir(codec, bnk, par, val);
+ anc_iir(component, bnk, par, val);
}
- dev_dbg(codec->dev, "%s: Exit.\n", __func__);
+ dev_dbg(component->dev, "%s: Exit.\n", __func__);
}
/*
@@ -1126,11 +1114,11 @@ static void anc_configure(struct snd_soc_codec *codec,
static int sid_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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.integer.value[0] = drvdata->sid_status;
+ ucontrol->value.enumerated.item[0] = drvdata->sid_status;
mutex_unlock(&drvdata->ctrl_lock);
return 0;
@@ -1140,15 +1128,15 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
static int sid_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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(codec->dev, "%s: Enter\n", __func__);
+ dev_dbg(component->dev, "%s: Enter\n", __func__);
- if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) {
- dev_err(codec->dev,
+ 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;
@@ -1156,10 +1144,10 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
mutex_lock(&drvdata->ctrl_lock);
- sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
+ sidconf = snd_soc_component_read(component, AB8500_SIDFIRCONF);
if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
- dev_err(codec->dev, "%s: Sidetone busy while off!\n",
+ dev_err(component->dev, "%s: Sidetone busy while off!\n",
__func__);
status = -EPERM;
} else {
@@ -1168,18 +1156,18 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
goto out;
}
- snd_soc_write(codec, AB8500_SIDFIRADR, 0);
+ snd_soc_component_write(component, AB8500_SIDFIRADR, 0);
for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
- val = snd_soc_read(codec, drvdata->sid_fir_values[param]);
- snd_soc_write(codec, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
- snd_soc_write(codec, AB8500_SIDFIRCOEF2, val & 0xff);
+ 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_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET),
BIT(AB8500_SIDFIRADR_FIRSIDSET));
- snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+ snd_soc_component_update_bits(component, AB8500_SIDFIRADR,
BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
drvdata->sid_status = SID_FIR_CONFIGURED;
@@ -1187,7 +1175,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
out:
mutex_unlock(&drvdata->ctrl_lock);
- dev_dbg(codec->dev, "%s: Exit\n", __func__);
+ dev_dbg(component->dev, "%s: Exit\n", __func__);
return status;
}
@@ -1195,11 +1183,11 @@ out:
static int anc_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+ 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.integer.value[0] = drvdata->anc_status;
+ ucontrol->value.enumerated.item[0] = drvdata->anc_status;
mutex_unlock(&drvdata->ctrl_lock);
return 0;
@@ -1208,9 +1196,10 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
static int anc_status_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
- struct device *dev = codec->dev;
+ 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;
@@ -1219,7 +1208,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
mutex_lock(&drvdata->ctrl_lock);
- req = ucontrol->value.integer.value[0];
+ req = ucontrol->value.enumerated.item[0];
if (req >= ARRAY_SIZE(enum_anc_state)) {
status = -EINVAL;
goto cleanup;
@@ -1234,17 +1223,16 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
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(&codec->dapm,
- "ANC Configure Input");
+ 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(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
- anc_configure(codec, apply_fir, apply_iir);
+ anc_configure(component, apply_fir, apply_iir);
if (apply_fir) {
if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1259,8 +1247,8 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
drvdata->anc_status = ANC_IIR_CONFIGURED;
}
- status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
- snd_soc_dapm_sync(&codec->dapm);
+ status = snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
+ snd_soc_dapm_sync(dapm);
cleanup:
mutex_unlock(&drvdata->ctrl_lock);
@@ -1291,8 +1279,8 @@ static int filter_control_info(struct snd_kcontrol *kcontrol,
static int filter_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1308,8 +1296,8 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
static int filter_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1335,11 +1323,10 @@ static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1);
static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1);
/* -1dB = Mute */
-static const unsigned int hs_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0)
+);
static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
@@ -1927,11 +1914,11 @@ enum ab8500_filter {
* Extended interface for codec-driver
*/
-static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
+static int ab8500_audio_init_audioblock(struct snd_soc_component *component)
{
int status;
- dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Reset audio-registers and disable 32kHz-clock output 2 */
status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
@@ -1944,25 +1931,26 @@ static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
return 0;
}
-static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
+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(codec->dev, "%s: Enter.\n", __func__);
+ dev_dbg(component->dev, "%s: Enter.\n", __func__);
/* Set DMic-clocks to outputs */
- status = abx500_get_register_interruptible(codec->dev, AB8500_MISC,
+ 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(codec->dev,
+ status = abx500_set_register_interruptible(component->dev,
AB8500_MISC,
AB8500_GPIO_DIR4_REG,
value);
@@ -1970,42 +1958,41 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
return status;
/* Attach regulators to AMic DAPM-paths */
- dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
+ 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(&codec->dapm, route, 1);
- dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
+ 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(&codec->dapm, route, 1);
- dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
+ 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(&codec->dapm, route, 1);
+ status |= snd_soc_dapm_add_routes(dapm, route, 1);
if (status < 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
__func__, status);
return status;
}
/* Set AMic-configuration */
- dev_dbg(codec->dev, "%s: Mic 1 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 1 mic-type: %s\n", __func__,
amic_type_str(amics->mic1_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ?
0 : AB8500_ANAGAINX_ENSEMICX);
- dev_dbg(codec->dev, "%s: Mic 2 mic-type: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Mic 2 mic-type: %s\n", __func__,
amic_type_str(amics->mic2_type));
- snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+ snd_soc_component_update_bits(component, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ?
0 : AB8500_ANAGAINX_ENSEMICX);
return 0;
}
-EXPORT_SYMBOL_GPL(ab8500_audio_setup_mics);
-static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
+static int ab8500_audio_set_ear_cmv(struct snd_soc_component *component,
enum ear_cm_voltage ear_cmv)
{
char *cmv_str;
@@ -2024,25 +2011,24 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
cmv_str = "1.58V";
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: Unknown earpiece CM-voltage (%d)!\n",
__func__, (int)ear_cmv);
return -EINVAL;
}
- dev_dbg(codec->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
+ dev_dbg(component->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
cmv_str);
- snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+ snd_soc_component_update_bits(component, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
ear_cmv);
return 0;
}
-EXPORT_SYMBOL_GPL(ab8500_audio_set_ear_cmv);
static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
unsigned int delay)
{
unsigned int mask, val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
val = 0;
@@ -2054,21 +2040,21 @@ static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported bit-delay (0x%x)!\n",
__func__, delay);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 Bit-delay: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 Bit-delay: %d bits.\n",
__func__, delay);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ 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_codec *codec,
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_component *component,
unsigned int fmt)
{
unsigned int mask;
@@ -2081,22 +2067,22 @@ static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
case SND_SOC_DAIFMT_CONT: /* continuous clock */
- dev_dbg(codec->dev, "%s: IF0 Clock is continuous.\n",
+ 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(codec->dev, "%s: IF0 Clock is gated.\n",
+ dev_dbg(component->dev, "%s: IF0 Clock is gated.\n",
__func__);
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"%s: ERROR: Unsupported clock mask (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
return 0;
}
@@ -2105,10 +2091,10 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
unsigned int mask;
unsigned int val;
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int status;
- dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+ dev_dbg(component->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
@@ -2116,35 +2102,35 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
BIT(AB8500_DIGIFCONF3_IF0MASTER);
val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 master.\n", __func__);
+ 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_CBS_CFS: /* codec clk & FRM slave */
- dev_dbg(dai->codec->dev,
- "%s: IF0 Master-mode: AB8500 slave.\n", __func__);
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dev_dbg(dai->component->dev,
+ "%s: IF0 Master-mode: AB8500 consumer.\n", __func__);
break;
- case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
- dev_err(dai->codec->dev,
- "%s: ERROR: The device is either a master or a slave.\n",
+ 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->codec->dev,
- "%s: ERROR: Unsupporter master mask 0x%x\n",
- __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ dev_err(dai->component->dev,
+ "%s: ERROR: Unsupporter clocking mask 0x%x\n",
+ __func__, fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
return -EINVAL;
- break;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF3, mask, val);
/* Set clock gating */
- status = ab8500_codec_set_dai_clock_gate(codec, fmt);
+ status = ab8500_codec_set_dai_clock_gate(component, fmt);
if (status) {
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Failed to set clock gate (%d).\n",
__func__, status);
return status;
@@ -2160,27 +2146,27 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S: /* I2S mode */
- dev_dbg(dai->codec->dev, "%s: IF0 Protocol: I2S\n", __func__);
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported format (0x%x)!\n",
__func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
return -EINVAL;
@@ -2188,37 +2174,37 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
- dev_dbg(dai->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ 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->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported INV mask 0x%x\n",
__func__, fmt & SND_SOC_DAIFMT_INV_MASK);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
return 0;
}
@@ -2227,7 +2213,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val, mask, slot, slots_active;
mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
@@ -2248,17 +2234,17 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF2_IF0WL0);
break;
default:
- dev_err(dai->codec->dev, "%s: Unsupported slot-width 0x%x\n",
+ dev_err(dai->component->dev, "%s: Unsupported slot-width 0x%x\n",
__func__, slot_width);
return -EINVAL;
}
- dev_dbg(dai->codec->dev, "%s: IF0 slot-width: %d bits.\n",
+ dev_dbg(dai->component->dev, "%s: IF0 slot-width: %d bits.\n",
__func__, slot_width);
- snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF2, mask, val);
/* Setup TDM clocking according to slot count */
- dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots);
+ dev_dbg(dai->component->dev, "%s: Slots, total: %d\n", __func__, slots);
mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
switch (slots) {
@@ -2276,12 +2262,12 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: ERROR: Unsupported number of slots (%d)!\n",
__func__, slots);
return -EINVAL;
}
- snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+ snd_soc_component_update_bits(component, AB8500_DIGIFCONF1, mask, val);
/* Setup TDM DA according to active tx slots */
@@ -2292,7 +2278,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
tx_mask = tx_mask << AB8500_DA_DATA0_OFFSET;
slots_active = hweight32(tx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, TX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2300,26 +2286,26 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(tx_mask);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ 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_update_bits(codec, AB8500_DASLOTCONF1, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, slot);
+ 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_update_bits(codec, AB8500_DASLOTCONF2, mask, slot);
- snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, slot);
+ 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->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode DA-from-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active TX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2333,7 +2319,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
rx_mask = rx_mask << AB8500_AD_DATA0_OFFSET;
slots_active = hweight32(rx_mask);
- dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
+ dev_dbg(dai->component->dev, "%s: Slots, active, RX: %d\n", __func__,
slots_active);
switch (slots_active) {
@@ -2341,29 +2327,29 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
break;
case 1:
slot = ffs(rx_mask);
- snd_soc_update_bits(codec, AB8500_ADSLOTSEL(slot),
+ 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_update_bits(codec,
+ 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_update_bits(codec,
+ 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->codec->dev,
+ dev_dbg(dai->component->dev,
"%s: In 8-channel mode AD-to-slot mapping is set manually.",
__func__);
break;
default:
- dev_err(dai->codec->dev,
+ dev_err(dai->component->dev,
"%s: Unsupported number of active RX-slots (%d)!\n",
__func__, slots_active);
return -EINVAL;
@@ -2389,7 +2375,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
},
{
.name = "ab8500-codec-dai.1",
@@ -2402,7 +2388,7 @@ static struct snd_soc_dai_driver ab8500_codec_dai[] = {
.formats = AB8500_SUPPORTED_FMT,
},
.ops = &ab8500_codec_ops,
- .symmetric_rates = 1
+ .symmetric_rate = 1
}
};
@@ -2411,28 +2397,28 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
{
u32 value;
- if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL))
+ 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_get_property(np, "stericsson,amic2-type-single-ended", NULL))
+ 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_get_property(np, "stericsson,amic1a-bias-vamic2", NULL))
+ 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_get_property(np, "stericsson,amic1b-bias-vamic2", NULL))
+ 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_get_property(np, "stericsson,amic2-bias-vamic1", NULL))
+ 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;
@@ -2461,57 +2447,33 @@ static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
}
}
-static int ab8500_codec_probe(struct snd_soc_codec *codec)
+static int ab8500_codec_probe(struct snd_soc_component *component)
{
- struct device *dev = codec->dev;
+ 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_platform_data *pdata;
+ struct ab8500_codec_platform_data codec_pdata;
struct filter_control *fc;
int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
- /* Setup AB8500 according to board-settings */
- pdata = dev_get_platdata(dev->parent);
-
- if (np) {
- if (!pdata)
- pdata = devm_kzalloc(dev,
- sizeof(struct ab8500_platform_data),
- GFP_KERNEL);
-
- if (pdata && !pdata->codec)
- pdata->codec
- = devm_kzalloc(dev,
- sizeof(struct ab8500_codec_platform_data),
- GFP_KERNEL);
-
- if (!(pdata && pdata->codec))
- return -ENOMEM;
-
- ab8500_codec_of_probe(dev, np, pdata->codec);
+ ab8500_codec_of_probe(dev, np, &codec_pdata);
- } else {
- if (!(pdata && pdata->codec)) {
- dev_err(dev, "No codec platform data or DT found\n");
- return -EINVAL;
- }
- }
-
- status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);
+ 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(codec, pdata->codec->ear_cmv);
+ 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(codec);
+ status = ab8500_audio_init_audioblock(component);
if (status < 0) {
dev_err(dev, "%s: failed to init audio-block (%d)!\n",
__func__, status);
@@ -2519,13 +2481,13 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
}
/* Override HW-defaults */
- snd_soc_write(codec, AB8500_ANACONF5,
+ snd_soc_component_write(component, AB8500_ANACONF5,
BIT(AB8500_ANACONF5_HSAUTOEN));
- snd_soc_write(codec, AB8500_SHORTCIRCONF,
+ snd_soc_component_write(component, AB8500_SHORTCIRCONF,
BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
/* Add filter controls */
- status = snd_soc_add_codec_controls(codec, ab8500_filter_controls,
+ status = snd_soc_add_component_controls(component, ab8500_filter_controls,
ARRAY_SIZE(ab8500_filter_controls));
if (status < 0) {
dev_err(dev,
@@ -2543,21 +2505,24 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
&ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
drvdata->sid_fir_values = (long *)fc->value;
- (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+ snd_soc_dapm_disable_pin(dapm, "ANC Configure Input");
mutex_init(&drvdata->ctrl_lock);
return status;
}
-static struct snd_soc_codec_driver ab8500_codec_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),
+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)
@@ -2586,7 +2551,8 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
}
dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
- status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
+ status = devm_snd_soc_register_component(&pdev->dev,
+ &ab8500_component_driver,
ab8500_codec_dai,
ARRAY_SIZE(ab8500_codec_dai));
if (status < 0)
@@ -2597,24 +2563,13 @@ static int ab8500_codec_driver_probe(struct platform_device *pdev)
return status;
}
-static int ab8500_codec_driver_remove(struct platform_device *pdev)
-{
- dev_dbg(&pdev->dev, "%s Enter.\n", __func__);
-
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver ab8500_codec_platform_driver = {
.driver = {
.name = "ab8500-codec",
},
.probe = ab8500_codec_driver_probe,
- .remove = ab8500_codec_driver_remove,
- .suspend = NULL,
- .resume = NULL,
};
module_platform_driver(ab8500_codec_platform_driver);
+MODULE_DESCRIPTION("ASoC AB8500 codec driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ab8500-codec.h b/sound/soc/codecs/ab8500-codec.h
index e2e54425d25e..2a6f6409f1f8 100644
--- a/sound/soc/codecs/ab8500-codec.h
+++ b/sound/soc/codecs/ab8500-codec.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) ST-Ericsson SA 2012
*
@@ -10,12 +11,6 @@
* Mikko J. Lehto <mikko.lehto@symbio.com>,
* Mikko Sarmanne <mikko.sarmanne@symbio.com>,
* for ST-Ericsson.
- *
- * License terms:
- *
- * 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 AB8500_CODEC_REGISTERS_H
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index d0ac723eee32..d8444a083af2 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ac97.c -- ALSA Soc AC97 codec support
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Generic AC97 support.
*/
@@ -17,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -36,18 +33,14 @@ static const struct snd_soc_dapm_route ac97_routes[] = {
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ 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);
}
-#define STD_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 ac97_dai_ops = {
.prepare = ac97_prepare,
};
@@ -58,18 +51,18 @@ static struct snd_soc_dai_driver ac97_dai = {
.stream_name = "AC97 Playback",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "AC97 Capture",
.channels_min = 1,
.channels_max = 2,
- .rates = STD_AC97_RATES,
+ .rates = SNDRV_PCM_RATE_KNOT,
.formats = SND_SOC_STD_AC97_FMTS,},
.ops = &ac97_dai_ops,
};
-static int ac97_soc_probe(struct snd_soc_codec *codec)
+static int ac97_soc_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
@@ -77,7 +70,7 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+ ret = snd_ac97_bus(component->card->snd_card, 0, soc_ac97_ops,
NULL, &ac97_bus);
if (ret < 0)
return ret;
@@ -87,25 +80,25 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
if (ret < 0)
return ret;
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_set_drvdata(component, ac97);
return 0;
}
#ifdef CONFIG_PM
-static int ac97_soc_suspend(struct snd_soc_codec *codec)
+static int ac97_soc_suspend(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
snd_ac97_suspend(ac97);
return 0;
}
-static int ac97_soc_resume(struct snd_soc_codec *codec)
+static int ac97_soc_resume(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
snd_ac97_resume(ac97);
@@ -116,36 +109,40 @@ static int ac97_soc_resume(struct snd_soc_codec *codec)
#define ac97_soc_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ac97, &ac97_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ac97, &ac97_dai, 1);
}
-static int ac97_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#ifdef CONFIG_OF
+static const struct of_device_id ac97_codec_of_match[] = {
+ { .compatible = "realtek,alc203", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac97_codec_of_match);
+#endif
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
+ .of_match_table = of_match_ptr(ac97_codec_of_match),
},
.probe = ac97_probe,
- .remove = ac97_remove,
};
module_platform_driver(ac97_codec_driver);
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 685998dd086e..949077108bef 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/init.h>
@@ -149,9 +148,9 @@ static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- /* ALCLK,ABCLK are both output, AD1836 can only be master */
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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;
@@ -164,7 +163,7 @@ 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_codec_get_drvdata(dai->codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(dai->component);
int word_len = 0;
/* bit size */
@@ -228,17 +227,17 @@ static struct snd_soc_dai_driver ad183x_dais[] = {
};
#ifdef CONFIG_PM
-static int ad1836_suspend(struct snd_soc_codec *codec)
+static int ad1836_suspend(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int ad1836_resume(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -248,10 +247,10 @@ static int ad1836_resume(struct snd_soc_codec *codec)
#define ad1836_resume NULL
#endif
-static int ad1836_probe(struct snd_soc_codec *codec)
+static int ad1836_probe(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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;
@@ -277,7 +276,7 @@ static int ad1836_probe(struct snd_soc_codec *codec)
if (ad1836->type == AD1836) {
/* left/right diff:PGA/MUX */
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x3A);
- ret = snd_soc_add_codec_controls(codec, ad1836_controls,
+ ret = snd_soc_add_component_controls(component, ad1836_controls,
ARRAY_SIZE(ad1836_controls));
if (ret)
return ret;
@@ -285,11 +284,11 @@ static int ad1836_probe(struct snd_soc_codec *codec)
regmap_write(ad1836->regmap, AD1836_ADC_CTRL3, 0x00);
}
- ret = snd_soc_add_codec_controls(codec, ad183x_dac_controls, num_dacs * 2);
+ ret = snd_soc_add_component_controls(component, ad183x_dac_controls, num_dacs * 2);
if (ret)
return ret;
- ret = snd_soc_add_codec_controls(codec, ad183x_adc_controls, num_adcs);
+ ret = snd_soc_add_component_controls(component, ad183x_adc_controls, num_adcs);
if (ret)
return ret;
@@ -306,33 +305,33 @@ static int ad1836_probe(struct snd_soc_codec *codec)
return ret;
ret = snd_soc_dapm_add_routes(dapm, ad183x_adc_routes, num_adcs);
- if (ret)
- return ret;
return ret;
}
/* power down chip */
-static int ad1836_remove(struct snd_soc_codec *codec)
+static void ad1836_remove(struct snd_soc_component *component)
{
- struct ad1836_priv *ad1836 = snd_soc_codec_get_drvdata(codec);
+ struct ad1836_priv *ad1836 = snd_soc_component_get_drvdata(component);
/* reset clock control mode */
- return regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
+ regmap_update_bits(ad1836->regmap, AD1836_ADC_CTRL2,
AD1836_ADC_SERFMT_MASK, 0);
}
-static struct snd_soc_codec_driver soc_codec_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),
+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[] = {
@@ -359,7 +358,7 @@ static const struct regmap_config ad1836_regmap_config = {
.max_register = AD1836_ADC_CTRL3,
.reg_defaults = ad1836_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(ad1836_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int ad1836_spi_probe(struct spi_device *spi)
@@ -380,17 +379,11 @@ static int ad1836_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ad1836);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_ad1836, &ad183x_dais[ad1836->type], 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_ad1836, &ad183x_dais[ad1836->type], 1);
return ret;
}
-static int ad1836_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id ad1836_ids[] = {
{ "ad1835", AD1835 },
{ "ad1836", AD1836 },
@@ -404,10 +397,8 @@ MODULE_DEVICE_TABLE(spi, ad1836_ids);
static struct spi_driver ad1836_spi_driver = {
.driver = {
.name = "ad1836",
- .owner = THIS_MODULE,
},
.probe = ad1836_spi_probe,
- .remove = ad1836_spi_remove,
.id_table = ad1836_ids,
};
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index dd7be0dbbc58..05711fab687a 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Audio Codec driver supporting:
* AD1835A, AD1836, AD1837A, AD1838A, AD1839A
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD1836_H__
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index df3a1a415825..6aa168e01fbb 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1936/AD1937 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -15,14 +14,13 @@
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", 0 },
- { "ad1937", 0 },
+ { "ad1936", AD193X },
+ { "ad1937", AD193X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
-static int ad193x_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ad193x_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -30,21 +28,16 @@ static int ad193x_i2c_probe(struct i2c_client *client,
config.val_bits = 8;
config.reg_bits = 8;
- return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
-}
-
-static int ad193x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return ad193x_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config),
+ (uintptr_t)i2c_get_match_data(client));
}
static struct i2c_driver ad193x_i2c_driver = {
.driver = {
.name = "ad193x",
},
- .probe = ad193x_i2c_probe,
- .remove = ad193x_i2c_remove,
+ .probe = ad193x_i2c_probe,
.id_table = ad193x_id,
};
module_i2c_driver(ad193x_i2c_driver);
diff --git a/sound/soc/codecs/ad193x-spi.c b/sound/soc/codecs/ad193x-spi.c
index 390cef9b9dc2..bce96a3d81c9 100644
--- a/sound/soc/codecs/ad193x-spi.c
+++ b/sound/soc/codecs/ad193x-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD1938/AD1939 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -16,6 +15,7 @@
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;
@@ -24,22 +24,27 @@ static int ad193x_spi_probe(struct spi_device *spi)
config.read_flag_mask = 0x09;
config.write_flag_mask = 0x08;
- return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+ return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+ (enum ad193x_type)id->driver_data);
}
-static int ad193x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
+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",
- .owner = THIS_MODULE,
},
.probe = ad193x_spi_probe,
- .remove = ad193x_spi_remove,
+ .id_table = ad193x_spi_id,
};
module_spi_driver(ad193x_spi_driver);
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 17c953595660..1d3c4d94b4ae 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD193X Audio Codec driver supporting AD1936/7/8/9
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -23,6 +22,7 @@
/* codec private data */
struct ad193x_priv {
struct regmap *regmap;
+ enum ad193x_type type;
int sysclk;
};
@@ -36,6 +36,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1,
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,
@@ -47,12 +54,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
- /* 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),
-
/* DAC switch control */
SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
AD193X_DACR1_MUTE, 1, 1),
@@ -63,53 +64,88 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
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),
-
- /* DAC de-emphasis */
- SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
};
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_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
- SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_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" },
- { "ADC", NULL, "SYSCLK" },
- { "DAC", NULL, "ADC_PWR" },
- { "ADC", NULL, "ADC_PWR" },
{ "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" },
- { "SYSCLK", NULL, "PLL_PWR" },
};
+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)
+static int ad193x_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL2,
@@ -125,7 +161,7 @@ static int ad193x_mute(struct snd_soc_dai *dai, int mute)
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_codec_get_drvdata(dai->codec);
+ struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(dai->component);
unsigned int channels;
switch (slots) {
@@ -147,8 +183,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
- AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_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;
}
@@ -156,23 +194,28 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec_dai->codec);
+ 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) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+ * 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:
- return -EINVAL;
+ if (ad193x_has_adc(ad193x))
+ return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -196,31 +239,39 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+ /* 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_CBS_CFM: /* codec clk slave & frm master */
+ case SND_SOC_DAIFMT_CBC_CFP:
adc_fmt |= AD193X_ADC_LCR_MASTER;
dac_fmt |= AD193X_DAC_LCR_MASTER;
break;
- case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+ case SND_SOC_DAIFMT_CBP_CFC:
adc_fmt |= AD193X_ADC_BCLK_MASTER;
dac_fmt |= AD193X_DAC_BCLK_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- 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);
+ 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);
@@ -230,8 +281,23 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -248,8 +314,15 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int word_len = 0, master_rate = 0;
- struct snd_soc_codec *codec = dai->codec;
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ 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)) {
@@ -280,6 +353,25 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
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);
@@ -287,18 +379,29 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
AD193X_DAC_WORD_LEN_MASK,
word_len << AD193X_DAC_WORD_LEN_SHFT);
- regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
- AD193X_ADC_WORD_LEN_MASK, word_len);
+ 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,
- .digital_mute = ad193x_mute,
+ .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 */
@@ -308,7 +411,7 @@ static struct snd_soc_dai_driver ad193x_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 8,
- .rates = SNDRV_PCM_RATE_48000,
+ .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,
},
@@ -323,51 +426,112 @@ static struct snd_soc_dai_driver ad193x_dai = {
.ops = &ad193x_dai_ops,
};
-static int ad193x_codec_probe(struct snd_soc_codec *codec)
+/* 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)
{
- struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+ 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));
+ }
+}
- /* default setting for ad193x */
+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;
- /* unmute dac channels */
- regmap_write(ad193x->regmap, AD193X_DAC_CHNL_MUTE, 0x0);
- /* de-emphasis: 48kHz, powedown dac */
- regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
- /* dac in tdm mode */
- regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
- /* high-pass filter enable */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
- /* sata delay=1, adc aux mode */
- regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
- /* pll input: mclki/xi */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
- regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
+ /* 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 struct snd_soc_codec_driver soc_codec_dev_ad193x = {
- .probe = ad193x_codec_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),
+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,
};
-static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
const struct regmap_config ad193x_regmap_config = {
.max_register = AD193X_NUM_REGS - 1,
- .volatile_reg = adau193x_reg_volatile,
};
EXPORT_SYMBOL_GPL(ad193x_regmap_config);
-int ad193x_probe(struct device *dev, struct regmap *regmap)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type)
{
struct ad193x_priv *ad193x;
@@ -379,11 +543,15 @@ int ad193x_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
ad193x->regmap = regmap;
+ ad193x->type = type;
dev_set_drvdata(dev, ad193x);
- return snd_soc_register_codec(dev, &soc_codec_dev_ad193x,
- &ad193x_dai, 1);
+ 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);
diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h
index ab9a998f15be..61f4648861d5 100644
--- a/sound/soc/codecs/ad193x.h
+++ b/sound/soc/codecs/ad193x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* AD193X Audio Codec driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __AD193X_H__
@@ -13,8 +12,15 @@
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);
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+ enum ad193x_type type);
#define AD193X_PLL_CLK_CTRL0 0x00
#define AD193X_PLL_POWERDOWN 0x01
@@ -24,8 +30,17 @@ int ad193x_probe(struct device *dev, struct regmap *regmap);
#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)
@@ -89,4 +104,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap);
#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
index 3cc69a626454..3c1ae13c1aae 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad1980.c -- ALSA Soc AD1980 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices Inc.
* Author: Roy Huang <roy.huang@analog.com>
* Cliff Cai <cliff.cai@analog.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
/*
@@ -96,7 +92,7 @@ static const struct regmap_config ad1980_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x7e,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
.readable_reg = ad1980_readable_reg,
@@ -202,37 +198,37 @@ static struct snd_soc_dai_driver ad1980_dai = {
.formats = SND_SOC_STD_AC97_FMTS, },
};
-static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
+#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_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
unsigned int retry_cnt = 0;
+ int ret;
do {
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(ac97);
- if (snd_soc_read(codec, AC97_RESET) == 0x0090)
- return 1;
- }
+ ret = snd_ac97_reset(ac97, true, AD1980_VENDOR_ID,
+ AD1980_VENDOR_MASK);
+ if (ret >= 0)
+ return 0;
- soc_ac97_ops->reset(ac97);
/*
* 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_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+ snd_soc_component_write(component, AC97_AD_SERIAL_CFG, 0x9900);
- if (snd_soc_read(codec, AC97_RESET) == 0x0090)
- return 0;
} while (retry_cnt++ < 10);
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+ dev_err(component->dev, "Failed to reset: AC97 link error\n");
return -EIO;
}
-static int ad1980_soc_probe(struct snd_soc_codec *codec)
+static int ad1980_soc_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
struct regmap *regmap;
@@ -240,10 +236,10 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
u16 vendor_id2;
u16 ext_status;
- ac97 = snd_soc_new_ac97_codec(codec);
+ ac97 = snd_soc_new_ac97_component(component, 0, 0);
if (IS_ERR(ac97)) {
ret = PTR_ERR(ac97);
- dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
+ dev_err(component->dev, "Failed to register AC97 component: %d\n", ret);
return ret;
}
@@ -253,82 +249,65 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
goto err_free_ac97;
}
- snd_soc_codec_init_regmap(codec, regmap);
- snd_soc_codec_set_drvdata(codec, ac97);
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
- ret = ad1980_reset(codec, 0);
+ ret = ad1980_reset(component, 0);
if (ret < 0)
goto reset_err;
- /* Read out vendor ID to make sure it is ad1980 */
- if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
- ret = -ENODEV;
- goto reset_err;
- }
-
- vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
-
- if (vendor_id2 != 0x5370) {
- if (vendor_id2 != 0x5374) {
- ret = -ENODEV;
- goto reset_err;
- } else {
- dev_warn(codec->dev,
- "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
- }
+ 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_write(codec, AC97_MASTER, 0x0000);
- snd_soc_write(codec, AC97_PCM, 0x0000);
- snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
- snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ 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_read(codec, AC97_EXTENDED_STATUS);
- snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
+ 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_codec_exit_regmap(codec);
+ snd_soc_component_exit_regmap(component);
err_free_ac97:
- snd_soc_free_ac97_codec(ac97);
+ snd_soc_free_ac97_component(ac97);
return ret;
}
-static int ad1980_soc_remove(struct snd_soc_codec *codec)
+static void ad1980_soc_remove(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- snd_soc_codec_exit_regmap(codec);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad1980, &ad1980_dai, 1);
-}
-
-static int ad1980_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad1980, &ad1980_dai, 1);
}
static struct platform_driver ad1980_codec_driver = {
@@ -337,7 +316,6 @@ static struct platform_driver ad1980_codec_driver = {
},
.probe = ad1980_probe,
- .remove = ad1980_remove,
};
module_platform_driver(ad1980_codec_driver);
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index a9400aef60b5..f6090ac57e93 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ad73311.c -- ALSA Soc AD73311 codec support
*
- * Copyright: Analog Device Inc.
+ * Copyright: Analog Devices Inc.
* Author: Cliff Cai <cliff.cai@analog.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/init.h>
@@ -54,23 +50,20 @@ static struct snd_soc_dai_driver ad73311_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE, },
};
-static struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ad73311, &ad73311_dai, 1);
-}
-
-static int ad73311_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ad73311, &ad73311_dai, 1);
}
static struct platform_driver ad73311_codec_driver = {
@@ -79,7 +72,6 @@ static struct platform_driver ad73311_codec_driver = {
},
.probe = ad73311_probe,
- .remove = ad73311_remove,
};
module_platform_driver(ad73311_codec_driver);
diff --git a/sound/soc/codecs/ad73311.h b/sound/soc/codecs/ad73311.h
index 4b353eefc0bf..774c62d561ba 100644
--- a/sound/soc/codecs/ad73311.h
+++ b/sound/soc/codecs/ad73311.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codec/ad73311.h
* Based on:
@@ -6,26 +7,10 @@
* Created: Thur Sep 25, 2008
* Description: definitions for AD73311 registers
*
- *
* Modified:
* Copyright 2006 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef __AD73311_H__
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..836940f2ab92
--- /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 <lars@metafoo.de>
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#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 <lars@metafoo.de>");
+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 000000000000..bf5947b35390
--- /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 000000000000..73f83be38f74
--- /dev/null
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#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" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
+
+static struct i2c_driver adau1372_i2c_driver = {
+ .driver = {
+ .name = "adau1372",
+ .of_match_table = adau1372_of_match,
+ },
+ .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 <lars@metafoo.de>");
+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 000000000000..656bd1fabeb3
--- /dev/null
+++ b/sound/soc/codecs/adau1372-spi.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for ADAU1372 codec
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#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",
+ .of_match_table = adau1372_of_match,
+ },
+ .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 <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
new file mode 100644
index 000000000000..fdee689cae53
--- /dev/null
+++ b/sound/soc/codecs/adau1372.c
@@ -0,0 +1,1073 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Analog Devices ADAU1372 Audio Codec driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#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);
+
+const struct of_device_id adau1372_of_match[] = {
+ { .compatible = "adi,adau1372" },
+ { }
+};
+EXPORT_SYMBOL_GPL(adau1372_of_match);
+MODULE_DEVICE_TABLE(of, adau1372_of_match);
+
+MODULE_DESCRIPTION("ASoC ADAU1372 CODEC driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau1372.h b/sound/soc/codecs/adau1372.h
new file mode 100644
index 000000000000..c55b1c24fe39
--- /dev/null
+++ b/sound/soc/codecs/adau1372.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ADAU1372 driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#ifndef SOUND_SOC_CODECS_ADAU1372_H
+#define SOUND_SOC_CODECS_ADAU1372_H
+
+#include <linux/regmap.h>
+
+struct device;
+
+extern const struct of_device_id adau1372_of_match[];
+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
index 783dcb57043a..16b9b2658341 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1,39 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Analog Devices ADAU1373 Audio Codec drive
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/property.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gcd.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/soc.h>
-#include <sound/adau1373.h>
#include "adau1373.h"
+#include "adau-utils.h"
struct adau1373_dai {
unsigned int clk_src;
unsigned int sysclk;
bool enable_src;
- bool master;
+ bool clock_provider;
+};
+
+enum adau1373_micbias_voltage {
+ ADAU1373_MICBIAS_2_9V,
+ ADAU1373_MICBIAS_2_2V,
+ ADAU1373_MICBIAS_2_6V,
+ ADAU1373_MICBIAS_1_8V,
};
+#define ADAU1373_DRC_SIZE 13
+
struct adau1373 {
struct regmap *regmap;
struct adau1373_dai dais[3];
+
+ bool input_differential[4];
+ bool lineout_differential;
+ bool lineout_ground_sense;
+
+ unsigned int num_drc;
+ u8 drc_setting[3][ADAU1373_DRC_SIZE];
+
+ enum adau1373_micbias_voltage micbias1;
+ enum adau1373_micbias_voltage micbias2;
};
#define ADAU1373_INPUT_MODE 0x00
@@ -320,13 +339,12 @@ static const struct reg_default adau1373_reg_defaults[] = {
{ ADAU1373_DIGEN, 0x00 },
};
-static const unsigned int adau1373_out_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+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),
-};
+ 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);
@@ -381,12 +399,11 @@ static const char *adau1373_bass_hpf_cutoff_text[] = {
"158Hz", "232Hz", "347Hz", "520Hz",
};
-static const unsigned int adau1373_bass_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+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),
-};
+ 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);
@@ -414,11 +431,10 @@ static SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
static SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
-static const unsigned int adau1373_3d_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 1, 7, TLV_DB_LINEAR_ITEM(-1800, -120)
+);
static const char *adau1373_lr_mux_text[] = {
"Mute",
@@ -551,8 +567,8 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -823,14 +839,14 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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].master)
+ if (!adau1373->dais[dai].clock_provider)
return 0;
if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
@@ -838,14 +854,14 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
else
clk = "SYSCLK2";
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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';
@@ -1033,8 +1049,8 @@ static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
static int adau1373_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1100,19 +1116,19 @@ static int adau1373_hw_params(struct snd_pcm_substream *substream,
static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl = ADAU1373_DAI_MASTER;
- adau1373_dai->master = true;
+ adau1373_dai->clock_provider = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl = 0;
- adau1373_dai->master = false;
+ adau1373_dai->clock_provider = false;
break;
default:
return -EINVAL;
@@ -1160,7 +1176,7 @@ static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(dai->component);
struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
switch (clk_id) {
@@ -1208,7 +1224,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 1,
@@ -1228,7 +1244,7 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.id = 2,
@@ -1248,16 +1264,17 @@ static struct snd_soc_dai_driver adau1373_dai_driver[] = {
.formats = ADAU1373_FORMATS,
},
.ops = &adau1373_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+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_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
unsigned int dpll_div = 0;
- unsigned int x, r, n, m, i, j, mode;
+ uint8_t pll_regs[5];
+ int ret;
switch (pll_id) {
case ADAU1373_PLL1:
@@ -1298,27 +1315,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
dpll_div++;
}
- if (freq_out % freq_in != 0) {
- /* fout = fin * (r + (n/m)) / x */
- x = DIV_ROUND_UP(freq_in, 13500000);
- freq_in /= x;
- r = freq_out / freq_in;
- i = freq_out % freq_in;
- j = gcd(i, freq_in);
- n = i / j;
- m = freq_in / j;
- x--;
- mode = 1;
- } else {
- /* fout = fin / r */
- r = freq_out / freq_in;
- n = 0;
- m = 0;
- x = 0;
- mode = 0;
- }
-
- if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+ ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+ if (ret)
return -EINVAL;
if (dpll_div) {
@@ -1333,12 +1331,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
(source << 4) | dpll_div);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
- (r << 3) | (x << 1) | mode);
+ 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);
@@ -1355,67 +1352,62 @@ static void adau1373_load_drc_settings(struct adau1373 *adau1373,
regmap_write(adau1373->regmap, ADAU1373_DRC(nr) + i, drc[i]);
}
-static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+static int adau1373_get_micbias(unsigned int val,
+ 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;
+ switch (val) {
+ case 2900000:
+ *micbias = ADAU1373_MICBIAS_2_9V;
+ return 0;
+ case 2200000:
+ *micbias = ADAU1373_MICBIAS_2_2V;
+ return 0;
+ case 2600000:
+ *micbias = ADAU1373_MICBIAS_2_6V;
+ return 0;
+ case 1800000:
+ *micbias = ADAU1373_MICBIAS_1_8V;
+ return 0;
default:
- break;
+ return -EINVAL;
}
- return false;
}
-static int adau1373_probe(struct snd_soc_codec *codec)
+static int adau1373_probe(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
- struct adau1373_platform_data *pdata = codec->dev->platform_data;
- bool lineout_differential = false;
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
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 < adau1373->num_drc; ++i) {
+ adau1373_load_drc_settings(adau1373, i,
+ adau1373->drc_setting[i]);
+ }
- 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,
+ adau1373->num_drc);
- snd_soc_add_codec_controls(codec, adau1373_drc_controls,
- pdata->num_drc);
+ val = 0;
+ for (i = 0; i < ARRAY_SIZE(adau1373->input_differential); ++i) {
+ if (adau1373->input_differential[i])
+ val |= BIT(i);
+ }
+ regmap_write(adau1373->regmap, ADAU1373_INPUT_MODE, val);
- 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 (adau1373->lineout_differential)
+ val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+ if (adau1373->lineout_ground_sense)
+ val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
- 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);
+ regmap_write(adau1373->regmap, ADAU1373_OUTPUT_CTRL, val);
- lineout_differential = pdata->lineout_differential;
+ regmap_write(adau1373->regmap, ADAU1373_EP_CTRL,
+ (adau1373->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+ (adau1373->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
- 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_codec_controls(codec, adau1373_lineout2_controls,
+ if (!adau1373->lineout_differential) {
+ snd_soc_add_component_controls(component, adau1373_lineout2_controls,
ARRAY_SIZE(adau1373_lineout2_controls));
}
@@ -1425,10 +1417,10 @@ static int adau1373_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adau1373_set_bias_level(struct snd_soc_codec *codec,
+static int adau1373_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1444,13 +1436,12 @@ static int adau1373_set_bias_level(struct snd_soc_codec *codec,
ADAU1373_PWDN_CTRL3_PWR_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int adau1373_resume(struct snd_soc_codec *codec)
+static int adau1373_resume(struct snd_soc_component *component)
{
- struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+ struct adau1373 *adau1373 = snd_soc_component_get_drvdata(component);
regcache_sync(adau1373->regmap);
@@ -1475,31 +1466,94 @@ static const struct regmap_config adau1373_regmap_config = {
.volatile_reg = adau1373_register_volatile,
.max_register = ADAU1373_SOFT_RESET,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1373_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1373_reg_defaults),
};
-static struct snd_soc_codec_driver adau1373_codec_driver = {
- .probe = adau1373_probe,
- .resume = adau1373_resume,
- .set_bias_level = adau1373_set_bias_level,
- .idle_bias_off = true,
+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,
+};
- .set_pll = adau1373_set_pll,
+static void adau1373_reset(void *reset_gpio)
+{
+ gpiod_set_value_cansleep(reset_gpio, 1);
+}
- .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),
-};
+static int adau1373_parse_fw(struct device *dev, struct adau1373 *adau1373)
+{
+ int ret, drc_count;
+ unsigned int val;
+
+ if (device_property_present(dev, "adi,input1-differential"))
+ adau1373->input_differential[0] = true;
+ if (device_property_present(dev, "adi,input2-differential"))
+ adau1373->input_differential[1] = true;
+ if (device_property_present(dev, "adi,input3-differential"))
+ adau1373->input_differential[2] = true;
+ if (device_property_present(dev, "adi,input4-differential"))
+ adau1373->input_differential[3] = true;
+
+ if (device_property_present(dev, "adi,lineout-differential"))
+ adau1373->lineout_differential = true;
+ if (device_property_present(dev, "adi,lineout-gnd-sense"))
+ adau1373->lineout_ground_sense = true;
+
+ ret = device_property_read_u32(dev, "adi,micbias1-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias1);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias1(%u)\n", val);
+ }
+
+ ret = device_property_read_u32(dev, "adi,micbias2-microvolt", &val);
+ if (!ret) {
+ ret = adau1373_get_micbias(val, &adau1373->micbias2);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get micbias2(%u)\n", val);
+ }
+
+ drc_count = device_property_count_u8(dev, "adi,drc-settings");
+ if (drc_count < 0)
+ return 0;
+ if (drc_count % ADAU1373_DRC_SIZE != 0)
+ return dev_err_probe(dev, -EINVAL,
+ "DRC count(%u) not multiple of %u\n",
+ drc_count, ADAU1373_DRC_SIZE);
+
+ adau1373->num_drc = drc_count / ADAU1373_DRC_SIZE;
+ if (adau1373->num_drc > ARRAY_SIZE(adau1373->drc_setting))
+ return dev_err_probe(dev, -EINVAL,
+ "Too many DRC settings(%u)\n",
+ adau1373->num_drc);
+
+ ret = device_property_read_u8_array(dev, "adi,drc-settings",
+ (u8 *)&adau1373->drc_setting[0],
+ drc_count);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to read DRC settings\n");
-static int adau1373_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ return 0;
+}
+
+static int adau1373_i2c_probe(struct i2c_client *client)
{
struct adau1373 *adau1373;
+ struct gpio_desc *gpiod;
int ret;
adau1373 = devm_kzalloc(&client->dev, sizeof(*adau1373), GFP_KERNEL);
@@ -1511,34 +1565,57 @@ static int adau1373_i2c_probe(struct i2c_client *client,
if (IS_ERR(adau1373->regmap))
return PTR_ERR(adau1373->regmap);
- regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ /*
+ * If the powerdown GPIO is specified, we use it for reset. Otherwise
+ * a software reset is done.
+ */
+ gpiod = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return PTR_ERR(gpiod);
+
+ if (gpiod) {
+ gpiod_set_value_cansleep(gpiod, 0);
+ fsleep(10);
+
+ ret = devm_add_action_or_reset(&client->dev, adau1373_reset,
+ gpiod);
+ if (ret)
+ return ret;
+ } else {
+ regmap_write(adau1373->regmap, ADAU1373_SOFT_RESET, 0x00);
+ }
dev_set_drvdata(&client->dev, adau1373);
- ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+ ret = adau1373_parse_fw(&client->dev, adau1373);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1373_component_driver,
adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
return ret;
}
-static int adau1373_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id adau1373_i2c_id[] = {
- { "adau1373", 0 },
+ { "adau1373" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+static const struct of_device_id adau1373_of_match[] = {
+ { .compatible = "adi,adau1373", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1373_of_match);
+
static struct i2c_driver adau1373_i2c_driver = {
.driver = {
.name = "adau1373",
- .owner = THIS_MODULE,
+ .of_match_table = adau1373_of_match,
},
.probe = adau1373_i2c_probe,
- .remove = adau1373_i2c_remove,
.id_table = adau1373_i2c_id,
};
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
index c6ab5530760c..56320d5e32d8 100644
--- a/sound/soc/codecs/adau1373.h
+++ b/sound/soc/codecs/adau1373.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ADAU1373_H__
#define __ADAU1373_H__
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index d4e219b6b98f..6876462d8bdb 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* based on an inital version by Cliff Cai <cliff.cai@analog.com>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -14,15 +13,15 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
#include "adau1701.h"
@@ -101,9 +100,13 @@
#define ADAU1701_FIRMWARE "adau1701.bin"
+static const char * const supply_names[] = {
+ "dvdd", "avdd"
+};
+
struct adau1701 {
- int gpio_nreset;
- int gpio_pll_mode[2];
+ struct gpio_desc *gpio_nreset;
+ struct gpio_descs *gpio_pll_mode;
unsigned int dai_fmt;
unsigned int pll_clkdiv;
unsigned int sysclk;
@@ -112,6 +115,7 @@ struct adau1701 {
u8 pin_config[12];
struct sigmadsp *sigmadsp;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -292,45 +296,45 @@ static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
.safeload = adau1701_safeload,
};
-static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+static int adau1701_reset(struct snd_soc_component *component, unsigned int clkdiv,
unsigned int rate)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
int ret;
+ DECLARE_BITMAP(values, 2);
sigmadsp_reset(adau1701->sigmadsp);
- if (clkdiv != ADAU1707_CLKDIV_UNSET &&
- gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
- gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ if (clkdiv != ADAU1707_CLKDIV_UNSET && adau1701->gpio_pll_mode) {
switch (clkdiv) {
case 64:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 0);
break;
case 256:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 0);
+ __assign_bit(1, values, 1);
break;
case 384:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 0);
break;
- case 0: /* fallback */
+ case 0: /* fallback */
case 512:
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
- gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
+ __assign_bit(0, values, 1);
+ __assign_bit(1, values, 1);
break;
}
+ gpiod_multi_set_value_cansleep(adau1701->gpio_pll_mode, values);
}
adau1701->pll_clkdiv = clkdiv;
- if (gpio_is_valid(adau1701->gpio_nreset)) {
- gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
+ if (adau1701->gpio_nreset) {
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 0);
/* minimum reset time is 20ns */
udelay(1);
- gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
+ gpiod_set_value_cansleep(adau1701->gpio_nreset, 1);
/* power-up time may be as long as 85ms */
mdelay(85);
}
@@ -342,7 +346,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
if (clkdiv != ADAU1707_CLKDIV_UNSET) {
ret = sigmadsp_setup(adau1701->sigmadsp, rate);
if (ret) {
- dev_warn(codec->dev, "Failed to load firmware\n");
+ dev_warn(component->dev, "Failed to load firmware\n");
return ret;
}
}
@@ -356,10 +360,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
return 0;
}
-static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_capture_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAU1701_SEROCTL_WORD_LEN_MASK;
unsigned int val;
@@ -397,10 +401,10 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
+static int adau1701_set_playback_pcm_format(struct snd_soc_component *component,
struct snd_pcm_hw_params *params)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adau1701->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
@@ -429,8 +433,8 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -441,7 +445,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
* firmware upload.
*/
if (clkdiv != adau1701->pll_clkdiv) {
- ret = adau1701_reset(codec, clkdiv, params_rate(params));
+ ret = adau1701_reset(component, clkdiv, params_rate(params));
if (ret < 0)
return ret;
}
@@ -464,26 +468,26 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
ADAU1701_DSPCTRL_SR_MASK, val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- return adau1701_set_playback_pcm_format(codec, params);
+ return adau1701_set_playback_pcm_format(component, params);
else
- return adau1701_set_capture_pcm_format(codec, params);
+ 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_codec *codec = codec_dai->codec;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -542,11 +546,11 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int adau1701_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -565,15 +569,14 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
+static int adau1701_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (mute)
@@ -586,11 +589,11 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
return 0;
}
-static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -613,7 +616,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
static int adau1701_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(dai->component);
return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
}
@@ -627,8 +630,9 @@ static int adau1701_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
- .digital_mute = adau1701_digital_mute,
+ .mute_stream = adau1701_mute_stream,
.startup = adau1701_startup,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver adau1701_dai = {
@@ -648,7 +652,7 @@ static struct snd_soc_dai_driver adau1701_dai = {
.formats = ADAU1701_FORMATS,
},
.ops = &adau1701_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_OF
@@ -659,16 +663,23 @@ static const struct of_device_id adau1701_dt_ids[] = {
MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
#endif
-static int adau1701_probe(struct snd_soc_codec *codec)
+static int adau1701_probe(struct snd_soc_component *component)
{
int i, ret;
unsigned int val;
- struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct adau1701 *adau1701 = snd_soc_component_get_drvdata(component);
- ret = sigmadsp_attach(adau1701->sigmadsp, &codec->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
@@ -678,9 +689,9 @@ static int adau1701_probe(struct snd_soc_codec *codec)
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
/* initalize with pre-configured pll mode settings */
- ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
+ ret = adau1701_reset(component, adau1701->pll_clkdiv, 0);
if (ret < 0)
- return ret;
+ goto exit_regulators_disable;
/* set up pin config */
val = 0;
@@ -696,67 +707,117 @@ static int adau1701_probe(struct snd_soc_codec *codec)
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 struct snd_soc_codec_driver adau1701_codec_drv = {
+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,
- .idle_bias_off = true,
-
.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_RBTREE,
+ .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,
- const struct i2c_device_id *id)
+static int adau1701_i2c_probe(struct i2c_client *client)
{
struct adau1701 *adau1701;
struct device *dev = &client->dev;
- int gpio_nreset = -EINVAL;
- int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
- int ret;
+ 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))
- return PTR_ERR(adau1701->regmap);
-
- if (dev->of_node) {
- gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
- if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
- return gpio_nreset;
-
- gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 0);
- if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
- return gpio_pll_mode[0];
+ if (IS_ERR(adau1701->regmap)) {
+ ret = PTR_ERR(adau1701->regmap);
+ goto exit_regulators_disable;
+ }
- gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
- "adi,pll-mode-gpios", 1);
- if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
- return gpio_pll_mode[1];
+ if (dev->of_node) {
of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
&adau1701->pll_clkdiv);
@@ -765,55 +826,44 @@ static int adau1701_i2c_probe(struct i2c_client *client,
ARRAY_SIZE(adau1701->pin_config));
}
- if (gpio_is_valid(gpio_nreset)) {
- ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
- "ADAU1701 Reset");
- if (ret < 0)
- return ret;
+ 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;
}
- if (gpio_is_valid(gpio_pll_mode[0]) &&
- gpio_is_valid(gpio_pll_mode[1])) {
- ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 0");
- if (ret < 0)
- return ret;
+ adau1701->gpio_pll_mode = devm_gpiod_get_array_optional(dev, "adi,pll-mode", GPIOD_OUT_LOW);
- ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
- GPIOF_OUT_INIT_LOW,
- "ADAU1701 PLL mode 1");
- if (ret < 0)
- return ret;
+ if (IS_ERR(adau1701->gpio_pll_mode)) {
+ ret = PTR_ERR(adau1701->gpio_pll_mode);
+ goto exit_regulators_disable;
}
- adau1701->gpio_nreset = gpio_nreset;
- adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
- adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
-
i2c_set_clientdata(client, adau1701);
adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
&adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
- if (IS_ERR(adau1701->sigmadsp))
- return PTR_ERR(adau1701->sigmadsp);
+ if (IS_ERR(adau1701->sigmadsp)) {
+ ret = PTR_ERR(adau1701->sigmadsp);
+ goto exit_regulators_disable;
+ }
- ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
+ ret = devm_snd_soc_register_component(&client->dev,
+ &adau1701_component_drv,
&adau1701_dai, 1);
- return ret;
-}
-static int adau1701_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+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 },
+ { "adau1401" },
+ { "adau1401a" },
+ { "adau1701" },
+ { "adau1702" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
@@ -821,11 +871,9 @@ MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
- .remove = adau1701_i2c_remove,
.id_table = adau1701_i2c_id,
};
diff --git a/sound/soc/codecs/adau1701.h b/sound/soc/codecs/adau1701.h
index 8d0949a2aec9..19b41e453a36 100644
--- a/sound/soc/codecs/adau1701.h
+++ b/sound/soc/codecs/adau1701.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAU1701 SigmaDSP processor
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAU1701_H
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 862796dec693..eba7e4f42c78 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1761.h"
-static int adau1761_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1761_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1761_i2c_probe(struct i2c_client *client,
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1761_i2c_remove(struct i2c_client *client)
+static void adau1761_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ adau17x1_remove(&client->dev);
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
@@ -44,10 +41,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = {
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
},
.probe = adau1761_i2c_probe,
.remove = adau1761_i2c_remove,
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index cce2f11f1ffb..7c9242c2ff94 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1761_spi_probe(struct spi_device *spi)
id->driver_data, adau1761_spi_switch_mode);
}
-static int adau1761_spi_remove(struct spi_device *spi)
+static void adau1761_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
+ adau17x1_remove(&spi->dev);
}
static const struct spi_device_id adau1761_spi_id[] = {
@@ -61,10 +59,21 @@ static const struct spi_device_id adau1761_spi_id[] = {
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1761_spi_dt_ids),
},
.probe = adau1761_spi_probe,
.remove = adau1761_spi_remove,
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index a1baeee160f4..1f09ea385f8a 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -29,6 +28,10 @@
#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
@@ -72,6 +75,10 @@ static const struct reg_default adau1761_reg_defaults[] = {
{ 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 },
@@ -122,6 +129,10 @@ 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,
};
@@ -148,6 +159,103 @@ 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),
@@ -162,6 +270,22 @@ static const struct snd_kcontrol_new adau1761_differential_mode_controls[] = {
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[] = {
@@ -255,8 +379,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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. */
@@ -432,8 +556,6 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Left DAC", NULL, "Interpolator Resync Clock" },
{ "Right DAC", NULL, "Interpolator Resync Clock" },
- { "DSP", NULL, "Digital Clock 0" },
-
{ "Slew Clock", NULL, "Digital Clock 0" },
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
@@ -445,10 +567,60 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Digital Clock 1", NULL, "SYSCLK" },
};
-static int adau1761_set_bias_level(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -456,24 +628,27 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
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;
}
- codec->dapm.bias_level = level;
return 0;
}
static enum adau1761_output_mode adau1761_get_lineout_mode(
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ struct adau1761_platform_data *pdata = component->dev->platform_data;
if (pdata)
return pdata->lineout_mode;
@@ -481,10 +656,11 @@ static enum adau1761_output_mode adau1761_get_lineout_mode(
return ADAU1761_OUTPUT_MODE_LINE;
}
-static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
+static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component)
{
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -509,27 +685,25 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
if (pdata->jackdetect_active_low)
val |= ADAU1761_DIGMIC_JACKDETECT_ACTIVE_LOW;
- ret = snd_soc_add_codec_controls(codec,
+ ret = snd_soc_add_component_controls(component,
adau1761_jack_detect_controls,
ARRAY_SIZE(adau1761_jack_detect_controls));
if (ret)
return ret;
- case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau1761_no_dmic_routes,
+ 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(&codec->dapm,
- adau1761_dmic_widgets,
+ 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(&codec->dapm,
- adau1761_dmic_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dmic_routes,
ARRAY_SIZE(adau1761_dmic_routes));
if (ret)
return ret;
@@ -545,10 +719,11 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_codec *codec)
return 0;
}
-static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
+static int adau1761_setup_headphone_mode(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
+ 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;
@@ -566,7 +741,7 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE,
ADAU1761_PLAY_MONO_OUTPUT_VOL_MODE_HP |
ADAU1761_PLAY_MONO_OUTPUT_VOL_UNMUTE);
- /* fallthrough */
+ fallthrough;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
regmap_update_bits(adau->regmap, ADAU1761_PLAY_HP_RIGHT_VOL,
ADAU1761_PLAY_HP_RIGHT_VOL_MODE_HP,
@@ -577,25 +752,25 @@ static int adau1761_setup_headphone_mode(struct snd_soc_codec *codec)
}
if (mode == ADAU1761_OUTPUT_MODE_HEADPHONE_CAPLESS) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ 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(&codec->dapm,
+ ret = snd_soc_dapm_add_routes(dapm,
adau1761_capless_dapm_routes,
ARRAY_SIZE(adau1761_capless_dapm_routes));
} else {
- ret = snd_soc_add_codec_controls(codec, adau1761_mono_controls,
+ 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(&codec->dapm,
+ 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(&codec->dapm,
+ ret = snd_soc_dapm_add_routes(dapm,
adau1761_mono_dapm_routes,
ARRAY_SIZE(adau1761_mono_dapm_routes));
}
@@ -630,6 +805,10 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
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;
@@ -638,13 +817,14 @@ static bool adau1761_readable_register(struct device *dev, unsigned int reg)
return adau17x1_readable_register(dev, reg);
}
-static int adau1761_codec_probe(struct snd_soc_codec *codec)
+static int adau1761_component_probe(struct snd_soc_component *component)
{
- struct adau1761_platform_data *pdata = codec->dev->platform_data;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ ret = adau17x1_add_widgets(component);
if (ret < 0)
return ret;
@@ -655,20 +835,20 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(adau->regmap, ADAU1761_RIGHT_DIFF_INPUT_VOL,
ADAU1761_DIFF_INPUT_VOL_LDEN,
ADAU1761_DIFF_INPUT_VOL_LDEN);
- ret = snd_soc_add_codec_controls(codec,
+ 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_codec_controls(codec,
+ 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(codec)) {
+ switch (adau1761_get_lineout_mode(component)) {
case ADAU1761_OUTPUT_MODE_LINE:
break;
case ADAU1761_OUTPUT_MODE_HEADPHONE:
@@ -683,47 +863,73 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
return -EINVAL;
}
- ret = adau1761_setup_headphone_mode(codec);
+ ret = adau1761_setup_headphone_mode(component);
if (ret)
return ret;
- ret = adau1761_setup_digmic_jackdetect(codec);
+ ret = adau1761_setup_digmic_jackdetect(component);
if (ret)
return ret;
- if (adau->type == ADAU1761) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
- adau1761_dapm_widgets,
+ /*
+ * 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(&codec->dapm,
- adau1761_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau1761_dapm_routes,
ARRAY_SIZE(adau1761_dapm_routes));
if (ret)
return ret;
}
-
- ret = adau17x1_add_routes(codec);
+ /*
+ * 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_codec_driver adau1761_codec_driver = {
- .probe = adau1761_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1761_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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 | \
@@ -786,7 +992,16 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
+ 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);
@@ -799,7 +1014,7 @@ const struct regmap_config adau1761_regmap_config = {
.readable_reg = adau1761_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1761_regmap_config);
diff --git a/sound/soc/codecs/adau1761.h b/sound/soc/codecs/adau1761.h
index a9e0d288301e..7beabf448ad1 100644
--- a/sound/soc/codecs/adau1761.h
+++ b/sound/soc/codecs/adau1761.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1361/ADAU1461/ADAU1761/ADAU1961 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1761_H__
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 2ce4362ccec1..cb67fde8d9a8 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1781.h"
-static int adau1781_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1781_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,12 @@ static int adau1781_i2c_probe(struct i2c_client *client,
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
-static int adau1781_i2c_remove(struct i2c_client *client)
+static void adau1781_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ adau17x1_remove(&client->dev);
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
@@ -42,10 +39,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = {
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
},
.probe = adau1781_i2c_probe,
.remove = adau1781_i2c_remove,
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 194686716bbe..1a09633d5a88 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADAU1381/ADAU1781 CODEC
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
@@ -46,10 +45,9 @@ static int adau1781_spi_probe(struct spi_device *spi)
id->driver_data, adau1781_spi_switch_mode);
}
-static int adau1781_spi_remove(struct spi_device *spi)
+static void adau1781_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
+ adau17x1_remove(&spi->dev);
}
static const struct spi_device_id adau1781_spi_id[] = {
@@ -59,10 +57,19 @@ static const struct spi_device_id adau1781_spi_id[] = {
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1781_spi_dt_ids),
},
.probe = adau1781_spi_probe,
.remove = adau1781_spi_remove,
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index 35581f43fa6d..faad2f9f8dd2 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
- * Driver for ADAU1781/ADAU1781 codec
+ * Driver for ADAU1381/ADAU1781 codec
*
* Copyright 2011-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -174,8 +173,8 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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. */
@@ -314,10 +313,10 @@ static const struct snd_soc_dapm_route adau1781_dmic_dapm_routes[] = {
{ "Right Decimator", NULL, "DMIC Select" },
};
-static int adau1781_set_bias_level(struct snd_soc_codec *codec,
+static int adau1781_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -339,7 +338,6 @@ static int adau1781_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -381,13 +379,14 @@ static int adau1781_set_input_mode(struct adau *adau, unsigned int reg,
ADAU1781_INPUT_DIFFERNTIAL, val);
}
-static int adau1781_codec_probe(struct snd_soc_codec *codec)
+static int adau1781_component_probe(struct snd_soc_component *component)
{
- struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ ret = adau17x1_add_widgets(component);
if (ret)
return ret;
@@ -403,43 +402,43 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
}
if (pdata && pdata->use_dmic) {
- ret = snd_soc_dapm_new_controls(&codec->dapm,
+ 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(&codec->dapm,
- adau1781_dmic_dapm_routes,
+ 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(&codec->dapm,
- adau1781_adc_dapm_routes,
+ 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(codec);
+ ret = adau17x1_add_routes(component);
if (ret < 0)
return ret;
return 0;
}
-static const struct snd_soc_codec_driver adau1781_codec_driver = {
- .probe = adau1781_codec_probe,
- .resume = adau17x1_resume,
- .set_bias_level = adau1781_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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 | \
@@ -473,7 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
.readable_reg = adau1781_readable_register,
.volatile_reg = adau17x1_volatile_register,
.precious_reg = adau17x1_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(adau1781_regmap_config);
@@ -498,7 +497,7 @@ int adau1781_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1781_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1781_component_driver,
&adau1781_dai_driver, 1);
}
EXPORT_SYMBOL_GPL(adau1781_probe);
diff --git a/sound/soc/codecs/adau1781.h b/sound/soc/codecs/adau1781.h
index 2b96e0a9ff2e..ac8b8acbdbbd 100644
--- a/sound/soc/codecs/adau1781.h
+++ b/sound/soc/codecs/adau1781.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1381/ADAU1781 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1781_H__
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fa2e690e51c8..0b6b0d2115eb 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -1,14 +1,14 @@
+// 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 <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -16,13 +16,20 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <linux/gcd.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
+#include <linux/unaligned.h>
#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",
@@ -58,12 +65,14 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- int ret;
+ 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;
@@ -76,7 +85,7 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
}
/* The PLL register is 6 bytes long and can only be written at once. */
- ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
if (SND_SOC_DAPM_EVENT_ON(event)) {
@@ -89,6 +98,27 @@ static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
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)",
@@ -120,7 +150,8 @@ static const struct snd_soc_dapm_widget adau17x1_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Right DAC Mode Mux", SND_SOC_NOPM, 0, 0,
&adau17x1_dac_mode_mux),
- SND_SOC_DAPM_ADC("Left Decimator", NULL, ADAU17X1_ADC_CONTROL, 0, 0),
+ 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),
@@ -154,10 +185,11 @@ static const struct snd_soc_dapm_route adau17x1_dapm_pll_route = {
static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
+ struct snd_soc_dapm_update update = {};
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
@@ -181,14 +213,14 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
else
reg = ADAU17X1_SERIAL_OUTPUT_ROUTE;
- change = snd_soc_test_bits(codec, reg, 0xff, val);
+ 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(&codec->dapm, kcontrol,
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
ucontrol->value.enumerated.item[0], e, &update);
}
@@ -198,8 +230,8 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -223,7 +255,7 @@ static int adau17x1_dsp_mux_enum_get(struct snd_kcontrol *kcontrol,
#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_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)
@@ -275,6 +307,7 @@ static const struct snd_soc_dapm_route adau17x1_dsp_dapm_routes[] = {
{ "DSP", NULL, "Left Decimator" },
{ "DSP", NULL, "Right Decimator" },
+ { "DSP", NULL, "Playback" },
};
static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
@@ -288,7 +321,7 @@ static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = {
{ "Capture", NULL, "Right Decimator" },
};
-bool adau17x1_has_dsp(struct adau *adau)
+static bool adau17x1_has_dsp(struct adau *adau)
{
switch (adau->type) {
case ADAU1761:
@@ -299,21 +332,161 @@ bool adau17x1_has_dsp(struct adau *adau)
return false;
}
}
-EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+
+/* 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_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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;
- if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+ 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;
- else
+ break;
+ default:
freq = adau->sysclk;
+ break;
+ }
if (freq % params_rate(params) != 0)
return -EINVAL;
@@ -353,13 +526,14 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(adau->regmap, ADAU17X1_CONVERTER0,
ADAU17X1_CONVERTER0_CONVSR_MASK, div);
- if (adau17x1_has_dsp(adau)) {
+
+ 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(adau, params_rate(params));
+ ret = adau17x1_setup_firmware(component, params_rate(params));
if (ret < 0)
return ret;
}
@@ -385,106 +559,20 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
-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_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- unsigned int r, n, m, i, j;
- unsigned int div;
- int ret;
-
- if (freq_in < 8000000 || freq_in > 27000000)
- return -EINVAL;
-
- 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;
- }
-
- adau->pll_regs[0] = m >> 8;
- adau->pll_regs[1] = m & 0xff;
- adau->pll_regs[2] = n >> 8;
- adau->pll_regs[3] = n & 0xff;
- adau->pll_regs[4] = (r << 3) | (div << 1);
- if (m != 0)
- adau->pll_regs[4] |= 1; /* Fractional mode */
-
- /* 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 adau *adau = snd_soc_codec_get_drvdata(dai->codec);
- struct snd_soc_dapm_context *dapm = &dai->codec->dapm;
-
- switch (clk_id) {
- case ADAU17X1_CLK_SRC_MCLK:
- case ADAU17X1_CLK_SRC_PLL:
- break;
- default:
- return -EINVAL;
- }
-
- adau->sysclk = freq;
-
- if (adau->clk_src != clk_id) {
- if (clk_id == ADAU17X1_CLK_SRC_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_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
ctrl0 = 0;
adau->master = false;
break;
@@ -536,8 +624,16 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
if (lrclk_pol)
ctrl0 |= ADAU17X1_SERIAL_PORT0_LRCLK_POL;
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT0, ctrl0);
- regmap_write(adau->regmap, ADAU17X1_SERIAL_PORT1, ctrl1);
+ /* 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;
@@ -547,7 +643,7 @@ static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
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_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
unsigned int ser_ctrl0, ser_ctrl1;
unsigned int conv_ctrl0, conv_ctrl1;
@@ -578,7 +674,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
switch (slot_width * slots) {
case 32:
- if (adau->type == ADAU1761)
+ if (adau->type == ADAU1761 || adau->type == ADAU1761_AS_1361)
return -EINVAL;
ser_ctrl1 = ADAU17X1_SERIAL_PORT1_BCLK32;
@@ -653,7 +749,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
regmap_update_bits(adau->regmap, ADAU17X1_SERIAL_PORT1,
ADAU17X1_SERIAL_PORT1_BCLK_MASK, ser_ctrl1);
- if (!adau17x1_has_dsp(adau))
+ if (!adau17x1_has_dsp(adau) && !adau17x1_has_disused_dsp(adau))
return 0;
if (adau->dsp_bypass[SNDRV_PCM_STREAM_PLAYBACK]) {
@@ -672,7 +768,7 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
static int adau17x1_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau *adau = snd_soc_component_get_drvdata(dai->component);
if (adau->sigmadsp)
return sigmadsp_restrict_params(adau->sigmadsp, substream);
@@ -690,10 +786,10 @@ const struct snd_soc_dai_ops adau17x1_dai_ops = {
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
-int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+int adau17x1_set_micbias_voltage(struct snd_soc_component *component,
enum adau17x1_micbias_voltage micbias)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
switch (micbias) {
case ADAU17X1_MICBIAS_0_90_AVDD:
@@ -779,46 +875,68 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
}
EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
+static int adau17x1_setup_firmware(struct snd_soc_component *component,
+ unsigned int rate)
{
int ret;
- int dspsr;
+ 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)
- return 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);
- return ret;
+ goto err;
}
regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dspsr);
+ regmap_write(adau->regmap, ADAU17X1_DSP_RUN, dsp_run);
- return 0;
+err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
-int adau17x1_add_widgets(struct snd_soc_codec *codec)
+int adau17x1_add_widgets(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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_codec_controls(codec, adau17x1_controls,
+ ret = snd_soc_add_component_controls(component, adau17x1_controls,
ARRAY_SIZE(adau17x1_controls));
if (ret)
return ret;
- ret = snd_soc_dapm_new_controls(&codec->dapm, adau17x1_dapm_widgets,
+ 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(&codec->dapm,
- adau17x1_dsp_dapm_widgets,
+ ret = snd_soc_dapm_new_controls(dapm, adau17x1_dsp_dapm_widgets,
ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
if (ret)
return ret;
@@ -826,9 +944,9 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
if (!adau->sigmadsp)
return 0;
- ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+ ret = sigmadsp_attach(adau->sigmadsp, component);
if (ret) {
- dev_err(codec->dev, "Failed to attach firmware: %d\n",
+ dev_err(component->dev, "Failed to attach firmware: %d\n",
ret);
return ret;
}
@@ -838,35 +956,38 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
-int adau17x1_add_routes(struct snd_soc_codec *codec)
+int adau17x1_add_routes(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ 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(&codec->dapm, adau17x1_dapm_routes,
+ 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(&codec->dapm,
- adau17x1_dsp_dapm_routes,
+ ret = snd_soc_dapm_add_routes(dapm, adau17x1_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_dsp_dapm_routes));
} else {
- ret = snd_soc_dapm_add_routes(&codec->dapm,
- adau17x1_no_dsp_dapm_routes,
+ 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_codec *codec)
+int adau17x1_resume(struct snd_soc_component *component)
{
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ struct adau *adau = snd_soc_component_get_drvdata(component);
if (adau->switch_mode)
- adau->switch_mode(codec->dev);
+ adau->switch_mode(component->dev);
regcache_sync(adau->regmap);
@@ -874,11 +995,62 @@ int adau17x1_resume(struct snd_soc_codec *codec)
}
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);
@@ -887,6 +1059,29 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
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;
@@ -894,8 +1089,13 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, adau);
if (firmware_name) {
- adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
- 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));
@@ -910,6 +1110,14 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
}
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 <lars@metafoo.de>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index e13583e6ff56..5e58abfffc3d 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ADAU17X1_H__
#define __ADAU17X1_H__
@@ -9,6 +10,7 @@
enum adau17x1_type {
ADAU1361,
ADAU1761,
+ ADAU1761_AS_1361,
ADAU1381,
ADAU1781,
};
@@ -22,13 +24,18 @@ enum adau17x1_pll_src {
};
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;
@@ -47,23 +54,21 @@ struct adau {
struct sigmadsp *sigmadsp;
};
-int adau17x1_add_widgets(struct snd_soc_codec *codec);
-int adau17x1_add_routes(struct snd_soc_codec *codec);
+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);
-int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
+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_codec *codec);
+int adau17x1_resume(struct snd_soc_component *component);
extern const struct snd_soc_dai_ops adau17x1_dai_ops;
-int adau17x1_setup_firmware(struct adau *adau, unsigned int rate);
-bool adau17x1_has_dsp(struct adau *adau);
-
#define ADAU17X1_CLOCK_CONTROL 0x4000
#define ADAU17X1_PLL_CONTROL 0x4002
#define ADAU17X1_REC_POWER_MGMT 0x4009
@@ -103,9 +108,9 @@ bool adau17x1_has_dsp(struct adau *adau);
#define ADAU17X1_CLOCK_CONTROL_CORECLK_SRC_PLL BIT(3)
#define ADAU17X1_CLOCK_CONTROL_SYSCLK_EN BIT(0)
-#define ADAU17X1_SERIAL_PORT1_BCLK32 (0x0 << 5)
-#define ADAU17X1_SERIAL_PORT1_BCLK48 (0x1 << 5)
-#define ADAU17X1_SERIAL_PORT1_BCLK64 (0x2 << 5)
+#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)
@@ -123,5 +128,7 @@ bool adau17x1_has_dsp(struct adau *adau);
#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
index 9700e8c838c9..441c8079246a 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/i2c.h>
@@ -15,8 +14,7 @@
#include "adau1977.h"
-static int adau1977_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adau1977_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
@@ -26,13 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client,
return adau1977_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
- id->driver_data, NULL);
-}
-
-static int adau1977_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ (uintptr_t)i2c_get_match_data(client), NULL);
}
static const struct i2c_device_id adau1977_i2c_ids[] = {
@@ -46,10 +38,8 @@ MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
static struct i2c_driver adau1977_i2c_driver = {
.driver = {
.name = "adau1977",
- .owner = THIS_MODULE,
},
.probe = adau1977_i2c_probe,
- .remove = adau1977_i2c_remove,
.id_table = adau1977_i2c_ids,
};
module_i2c_driver(adau1977_i2c_driver);
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index b05cf5da3a94..e7e95e5d1911 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -1,15 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
+#include <linux/of.h>
#include <linux/spi/spi.h>
#include <sound/soc.h>
@@ -46,12 +46,6 @@ static int adau1977_spi_probe(struct spi_device *spi)
id->driver_data, adau1977_spi_switch_mode);
}
-static int adau1977_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct spi_device_id adau1977_spi_ids[] = {
{ "adau1977", ADAU1977 },
{ "adau1978", ADAU1978 },
@@ -60,13 +54,20 @@ static const struct spi_device_id adau1977_spi_ids[] = {
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1977_spi_of_match),
},
.probe = adau1977_spi_probe,
- .remove = adau1977_spi_remove,
.id_table = adau1977_spi_ids,
};
module_spi_driver(adau1977_spi_driver);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 70ab35744aba..c193a9f22f59 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/delay.h>
@@ -13,7 +12,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/platform_data/adau1977.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -25,6 +23,8 @@
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <dt-bindings/sound/adi,adau1977.h>
+
#include "adau1977.h"
#define ADAU1977_REG_POWER 0x00
@@ -124,10 +124,10 @@ struct adau1977 {
struct device *dev;
void (*switch_mode)(struct device *dev);
- unsigned int max_master_fs;
+ unsigned int max_clock_provider_fs;
unsigned int slot_width;
bool enabled;
- bool master;
+ bool clock_provider;
};
static const struct reg_default adau1977_reg_defaults[] = {
@@ -202,7 +202,7 @@ static const struct snd_soc_dapm_route adau1977_dapm_routes[] = {
ADAU1977_REG_DC_HPF_CAL, (x) - 1, 1, 0)
#define ADAU1977_DC_SUB_SWITCH(x) \
- SOC_SINGLE("ADC" #x " DC Substraction Capture Switch", \
+ 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[] = {
@@ -236,8 +236,6 @@ static int adau1977_reset(struct adau1977 *adau1977)
ret = regmap_write(adau1977->regmap, ADAU1977_REG_POWER,
ADAU1977_POWER_RESET);
regcache_cache_bypass(adau1977->regmap, false);
- if (ret)
- return ret;
return ret;
}
@@ -294,8 +292,8 @@ static int adau1977_lookup_mcs(struct adau1977 *adau1977, unsigned int rate,
static int adau1977_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -332,7 +330,7 @@ static int adau1977_hw_params(struct snd_pcm_substream *substream,
ctrl0_mask |= ADAU1977_SAI_CTRL0_FMT_MASK;
}
- if (adau1977->master) {
+ if (adau1977->clock_provider) {
switch (params_width(params)) {
case 16:
ctrl1 = ADAU1977_SAI_CTRL1_DATA_WIDTH_16BIT;
@@ -388,8 +386,7 @@ static int adau1977_power_disable(struct adau1977 *adau1977)
regcache_mark_dirty(adau1977->regmap);
- if (adau1977->reset_gpio)
- gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 0);
regcache_cache_only(adau1977->regmap, true);
@@ -420,8 +417,7 @@ static int adau1977_power_enable(struct adau1977 *adau1977)
goto err_disable_avdd;
}
- if (adau1977->reset_gpio)
- gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
+ gpiod_set_value_cansleep(adau1977->reset_gpio, 1);
regcache_cache_only(adau1977->regmap, false);
@@ -473,10 +469,10 @@ err_disable_avdd:
return ret;
}
-static int adau1977_set_bias_level(struct snd_soc_codec *codec,
+static int adau1977_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@@ -485,7 +481,7 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = adau1977_power_enable(adau1977);
break;
case SND_SOC_BIAS_OFF:
@@ -493,18 +489,13 @@ static int adau1977_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ 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_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl0, ctrl1, drv;
unsigned int slot[4];
unsigned int i;
@@ -513,7 +504,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
if (slots == 0) {
/* 0 = No fixed slot width */
adau1977->slot_width = 0;
- adau1977->max_master_fs = 192000;
+ 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);
@@ -542,7 +533,7 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 24:
/* We can only generate 16 bit or 32 bit wide slots */
- if (adau1977->master)
+ if (adau1977->clock_provider)
return -EINVAL;
ctrl1 = ADAU1977_SAI_CTRL1_SLOT_WIDTH_24;
break;
@@ -602,15 +593,15 @@ static int adau1977_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
adau1977->slot_width = width;
- /* In master mode the maximum bitclock is 24.576 MHz */
- adau1977->max_master_fs = min(192000, 24576000 / width / slots);
+ /* 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_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -624,18 +615,18 @@ static int adau1977_mute(struct snd_soc_dai *dai, int mute, int stream)
static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- adau1977->master = false;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ adau1977->clock_provider = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
ctrl1 |= ADAU1977_SAI_CTRL1_MASTER;
- adau1977->master = true;
+ adau1977->clock_provider = true;
break;
default:
return -EINVAL;
@@ -711,7 +702,7 @@ static int adau1977_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int adau1977_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
u64 formats = 0;
if (adau1977->slot_width == 16)
@@ -723,9 +714,10 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &adau1977->constraints);
- if (adau1977->master)
+ if (adau1977->clock_provider)
snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, 8000, adau1977->max_master_fs);
+ SNDRV_PCM_HW_PARAM_RATE, 8000,
+ adau1977->max_clock_provider_fs);
if (formats != 0)
snd_pcm_hw_constraint_mask64(substream->runtime,
@@ -736,7 +728,7 @@ static int adau1977_startup(struct snd_pcm_substream *substream,
static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(dai->codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (tristate)
@@ -797,13 +789,13 @@ static bool adau1977_check_sysclk(unsigned int mclk, unsigned int base_freq)
return true;
}
-static int adau1977_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adau1977 *adau1977 = snd_soc_component_get_drvdata(component);
unsigned int mask = 0;
unsigned int clk_src;
- unsigned int ret;
+ int ret;
if (dir != SND_SOC_CLOCK_IN)
return -EINVAL;
@@ -851,14 +843,15 @@ static int adau1977_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adau1977_codec_probe(struct snd_soc_codec *codec)
+static int adau1977_component_probe(struct snd_soc_component *component)
{
- struct adau1977 *adau1977 = snd_soc_codec_get_drvdata(codec);
+ 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(&codec->dapm,
+ ret = snd_soc_dapm_new_controls(dapm,
adau1977_micbias_dapm_widgets,
ARRAY_SIZE(adau1977_micbias_dapm_widgets));
if (ret < 0)
@@ -871,32 +864,30 @@ static int adau1977_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver adau1977_codec_driver = {
- .probe = adau1977_codec_probe,
- .set_bias_level = adau1977_set_bias_level,
- .set_sysclk = adau1977_set_sysclk,
- .idle_bias_off = true,
-
- .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),
+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)
{
- struct adau1977_platform_data *pdata = adau1977->dev->platform_data;
unsigned int micbias;
- if (pdata) {
- micbias = pdata->micbias;
- if (micbias > ADAU1977_MICBIAS_9V0)
- return -EINVAL;
-
- } else {
+ 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,
@@ -922,7 +913,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->type = type;
adau1977->regmap = regmap;
adau1977->switch_mode = switch_mode;
- adau1977->max_master_fs = 192000;
+ adau1977->max_clock_provider_fs = 192000;
adau1977->constraints.list = adau1977_rates;
adau1977->constraints.count = ARRAY_SIZE(adau1977_rates);
@@ -938,22 +929,15 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
adau1977->dvdd_reg = NULL;
}
- adau1977->reset_gpio = devm_gpiod_get(dev, "reset");
- if (IS_ERR(adau1977->reset_gpio)) {
- ret = PTR_ERR(adau1977->reset_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return PTR_ERR(adau1977->reset_gpio);
- adau1977->reset_gpio = 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) {
- ret = gpiod_direction_output(adau1977->reset_gpio, 0);
- if (ret)
- return ret;
+ if (adau1977->reset_gpio)
ndelay(100);
- }
ret = adau1977_power_enable(adau1977);
if (ret)
@@ -979,7 +963,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
if (ret)
return ret;
- return snd_soc_register_codec(dev, &adau1977_codec_driver,
+ return devm_snd_soc_register_component(dev, &adau1977_component_driver,
&adau1977_dai, 1);
err_poweroff:
@@ -1007,7 +991,7 @@ const struct regmap_config adau1977_regmap_config = {
.max_register = ADAU1977_REG_DC_HPF_CAL,
.volatile_reg = adau1977_register_volatile,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adau1977_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adau1977_reg_defaults),
};
diff --git a/sound/soc/codecs/adau1977.h b/sound/soc/codecs/adau1977.h
index 95e714345a86..80baeb4f39b9 100644
--- a/sound/soc/codecs/adau1977.h
+++ b/sound/soc/codecs/adau1977.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADAU1977/ADAU1978/ADAU1979 driver
*
* Copyright 2014 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SOUND_SOC_CODECS_ADAU1977_H__
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..c9134e1de0b2
--- /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 <lars@metafoo.de>
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+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 <lars@metafoo.de>");
+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 000000000000..45a5d2dcc0f2
--- /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 <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+
+#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 <nuno.sa@analog.com>");
+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 000000000000..f9dc8f4ef9a4
--- /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 <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#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"},
+ {}
+};
+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 <nuno.sa@analog.com>");
+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 000000000000..14259807c872
--- /dev/null
+++ b/sound/soc/codecs/adau7118.c
@@ -0,0 +1,576 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Analog Devices ADAU7118 8 channel PDM-to-I2S/TDM Converter driver
+//
+// Copyright 2019 Analog Devices Inc.
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#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,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const 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;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ 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 <nuno.sa@analog.com>");
+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 000000000000..c65679a4dff1
--- /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
index 790fce33ab10..f734c71211da 100644
--- a/sound/soc/codecs/adav801.c
+++ b/sound/soc/codecs/adav801.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV801 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,19 +29,11 @@ static int adav80x_spi_probe(struct spi_device *spi)
return adav80x_bus_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int adav80x_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver adav80x_spi_driver = {
.driver = {
.name = "adav801",
- .owner = THIS_MODULE,
},
.probe = adav80x_spi_probe,
- .remove = adav80x_spi_remove,
.id_table = adav80x_spi_id,
};
module_spi_driver(adav80x_spi_driver);
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
index 66d9fce34e62..8b96c41f0354 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADAV803 audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -15,31 +14,22 @@
#include "adav80x.h"
static const struct i2c_device_id adav803_id[] = {
- { "adav803", 0 },
+ { "adav803" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
-static int adav803_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int adav803_probe(struct i2c_client *client)
{
return adav80x_bus_probe(&client->dev,
devm_regmap_init_i2c(client, &adav80x_regmap_config));
}
-static int adav803_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver adav803_driver = {
.driver = {
.name = "adav803",
- .owner = THIS_MODULE,
},
.probe = adav803_probe,
- .remove = adav803_remove,
.id_table = adav803_id,
};
module_i2c_driver(adav803_driver);
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index b67480f1b1aa..c8c0fc928211 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADAV80X Audio Codec driver supporting ADAV801, ADAV803
*
* Copyright 2011 Analog Devices Inc.
* Author: Yi Li <yi.li@analog.com>
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/module.h>
@@ -113,7 +112,7 @@
#define ADAV80X_PLL_OUTE_SYSCLKPD(x) BIT(2 - (x))
-static struct reg_default adav80x_reg_defaults[] = {
+static const struct reg_default adav80x_reg_defaults[] = {
{ ADAV80X_PLAYBACK_CTRL, 0x01 },
{ ADAV80X_AUX_IN_CTRL, 0x01 },
{ ADAV80X_REC_CTRL, 0x02 },
@@ -212,8 +211,8 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -230,14 +229,14 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
return 0;
}
- return strcmp(source->name, clk) == 0;
+ return snd_soc_dapm_widget_name_cmp(source, clk) == 0;
}
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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;
}
@@ -281,9 +280,9 @@ static const struct snd_soc_dapm_route adav80x_dapm_routes[] = {
{ "AIFAUXIN", NULL, "SYSCLK" },
};
-static int adav80x_set_deemph(struct snd_soc_codec *codec)
+static int adav80x_set_deemph(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->deemph) {
@@ -315,25 +314,25 @@ static int adav80x_set_deemph(struct snd_soc_codec *codec)
static int adav80x_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
- unsigned int deemph = ucontrol->value.enumerated.item[0];
+ 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(codec);
+ return adav80x_set_deemph(component);
}
static int adav80x_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = adav80x->deemph;
+ ucontrol->value.integer.value[0] = adav80x->deemph;
return 0;
};
@@ -365,16 +364,17 @@ static unsigned int adav80x_port_ctrl_regs[2][2] = {
static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
capture |= ADAV80X_CAPTURE_MODE_MASTER;
playback |= ADAV80X_PLAYBACK_MODE_MASTER;
- case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -415,10 +415,10 @@ static int adav80x_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
+static int adav80x_set_adc_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -432,10 +432,10 @@ static int adav80x_set_adc_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
+static int adav80x_set_dac_clock(struct snd_soc_component *component,
unsigned int sample_rate)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (sample_rate <= 48000)
@@ -450,10 +450,10 @@ static int adav80x_set_dac_clock(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
switch (params_width(params)) {
@@ -479,10 +479,10 @@ static int adav80x_set_capture_pcm_format(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int val;
if (adav80x->dai_fmt[dai->id] != SND_SOC_DAIFMT_RIGHT_J)
@@ -514,32 +514,32 @@ static int adav80x_set_playback_pcm_format(struct snd_soc_codec *codec,
static int adav80x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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(codec, dai, params);
- adav80x_set_dac_clock(codec, rate);
+ adav80x_set_playback_pcm_format(component, dai, params);
+ adav80x_set_dac_clock(component, rate);
} else {
- adav80x_set_capture_pcm_format(codec, dai, params);
- adav80x_set_adc_clock(codec, rate);
+ adav80x_set_capture_pcm_format(component, dai, params);
+ adav80x_set_adc_clock(component, rate);
}
adav80x->rate = rate;
- adav80x_set_deemph(codec);
+ adav80x_set_deemph(component);
return 0;
}
-static int adav80x_set_sysclk(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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) {
@@ -619,10 +619,11 @@ static int adav80x_set_sysclk(struct snd_soc_codec *codec,
return 0;
}
-static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
+static int adav80x_set_pll(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -647,6 +648,7 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
pll_ctrl1 |= ADAV80X_PLL_CTRL1_PLLDIV;
break;
}
+ fallthrough;
default:
return -EINVAL;
}
@@ -687,16 +689,16 @@ static int adav80x_set_pll(struct snd_soc_codec *codec, int pll_id,
adav80x->pll_src = source;
- snd_soc_dapm_sync(&codec->dapm);
+ snd_soc_dapm_sync(dapm);
}
return 0;
}
-static int adav80x_set_bias_level(struct snd_soc_codec *codec,
+static int adav80x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
unsigned int mask = ADAV80X_DAC_CTRL1_PD;
switch (level) {
@@ -714,7 +716,6 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -722,23 +723,23 @@ static int adav80x_set_bias_level(struct snd_soc_codec *codec,
static int adav80x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
+ if (!snd_soc_component_active(component) || !adav80x->rate)
return 0;
- return snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+ 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_codec *codec = dai->codec;
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
adav80x->rate = 0;
}
@@ -799,13 +800,14 @@ static struct snd_soc_dai_driver adav80x_dais[] = {
},
};
-static int adav80x_probe(struct snd_soc_codec *codec)
+static int adav80x_probe(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ 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(&codec->dapm, "PLL1");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL2");
+ 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);
@@ -815,30 +817,31 @@ static int adav80x_probe(struct snd_soc_codec *codec)
return 0;
}
-static int adav80x_resume(struct snd_soc_codec *codec)
+static int adav80x_resume(struct snd_soc_component *component)
{
- struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
+ struct adav80x *adav80x = snd_soc_component_get_drvdata(component);
regcache_sync(adav80x->regmap);
return 0;
}
-static struct snd_soc_codec_driver adav80x_codec_driver = {
- .probe = adav80x_probe,
- .resume = adav80x_resume,
- .set_bias_level = adav80x_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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)
@@ -855,7 +858,7 @@ int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, adav80x);
adav80x->regmap = regmap;
- return snd_soc_register_codec(dev, &adav80x_codec_driver,
+ return devm_snd_soc_register_component(dev, &adav80x_component_driver,
adav80x_dais, ARRAY_SIZE(adav80x_dais));
}
EXPORT_SYMBOL_GPL(adav80x_bus_probe);
@@ -864,11 +867,10 @@ const struct regmap_config adav80x_regmap_config = {
.val_bits = 8,
.pad_bits = 1,
.reg_bits = 7,
- .read_flag_mask = 0x01,
.max_register = ADAV80X_PLL_OUTE,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = adav80x_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(adav80x_reg_defaults),
};
diff --git a/sound/soc/codecs/adav80x.h b/sound/soc/codecs/adav80x.h
index 8a1d7c09dca5..fb50ff57e317 100644
--- a/sound/soc/codecs/adav80x.h
+++ b/sound/soc/codecs/adav80x.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* header file for ADAV80X parts
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef _ADAV80X_H
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index 1222282e93c3..44aa06e03486 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ads117x.c -- Driver for ads1174/8 ADC chips
*
* Copyright 2009 ShotSpotter Inc.
* Author: Graeme Gregory <gg@slimlogic.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/kernel.h>
@@ -20,6 +16,8 @@
#include <sound/initval.h>
#include <sound/soc.h>
+#include <linux/of.h>
+
#define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
#define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
@@ -56,32 +54,38 @@ static struct snd_soc_dai_driver ads117x_dai = {
.formats = ADS117X_FORMATS,},
};
-static struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ads117x, &ads117x_dai, 1);
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ads117x, &ads117x_dai, 1);
}
-static int ads117x_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
+#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,
- .remove = ads117x_remove,
};
module_platform_driver(ads117x_codec_driver);
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 1fd7f72b2a62..a33cb329865c 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AK4104 ALSA SoC (ASoC) driver
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <sound/asoundef.h>
#include <sound/core.h>
@@ -65,8 +61,8 @@ static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int val = 0;
int ret;
@@ -81,12 +77,12 @@ static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
+ dev_err(component->dev, "invalid dai format\n");
return -EINVAL;
}
- /* This device can only be slave */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ /* 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,
@@ -102,8 +98,8 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -141,7 +137,7 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
val |= IEC958_AES3_CON_FS_192000;
break;
default:
- dev_err(codec->dev, "unsupported sampling rate\n");
+ dev_err(component->dev, "unsupported sampling rate\n");
return -EINVAL;
}
@@ -163,7 +159,10 @@ static struct snd_soc_dai_driver ak4104_dai = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
+ .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
@@ -171,14 +170,14 @@ static struct snd_soc_dai_driver ak4104_dai = {
.ops = &ak4101_dai_ops,
};
-static int ak4104_probe(struct snd_soc_codec *codec)
+static int ak4104_probe(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *ak4104 = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(ak4104->regulator);
if (ret < 0) {
- dev_err(codec->dev, "Unable to enable regulator: %d\n", ret);
+ dev_err(component->dev, "Unable to enable regulator: %d\n", ret);
return ret;
}
@@ -202,30 +201,28 @@ exit_disable_regulator:
return ret;
}
-static int ak4104_remove(struct snd_soc_codec *codec)
+static void ak4104_remove(struct snd_soc_component *component)
{
- struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
+ 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);
-
- return 0;
}
#ifdef CONFIG_PM
-static int ak4104_soc_suspend(struct snd_soc_codec *codec)
+static int ak4104_soc_suspend(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
regulator_disable(priv->regulator);
return 0;
}
-static int ak4104_soc_resume(struct snd_soc_codec *codec)
+static int ak4104_soc_resume(struct snd_soc_component *component)
{
- struct ak4104_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak4104_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_enable(priv->regulator);
@@ -239,16 +236,18 @@ static int ak4104_soc_resume(struct snd_soc_codec *codec)
#define ak4104_soc_resume NULL
#endif /* CONFIG_PM */
-static struct snd_soc_codec_driver soc_codec_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),
+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 = {
@@ -264,8 +263,8 @@ static const struct regmap_config ak4104_regmap = {
static int ak4104_spi_probe(struct spi_device *spi)
{
- struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
+ struct gpio_desc *reset_gpiod;
unsigned int val;
int ret;
@@ -293,19 +292,10 @@ static int ak4104_spi_probe(struct spi_device *spi)
return ret;
}
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&spi->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "ak4104 reset");
- if (ret < 0)
- 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
@@ -318,17 +308,11 @@ static int ak4104_spi_probe(struct spi_device *spi)
spi_set_drvdata(spi, ak4104);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_device_ak4104, &ak4104_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_device_ak4104, &ak4104_dai, 1);
return ret;
}
-static int ak4104_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ak4104_of_match[] = {
{ .compatible = "asahi-kasei,ak4104", },
{ }
@@ -344,12 +328,10 @@ MODULE_DEVICE_TABLE(spi, ak4104_id_table);
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = "ak4104",
- .owner = THIS_MODULE,
.of_match_table = ak4104_of_match,
},
.id_table = ak4104_id_table,
.probe = ak4104_spi_probe,
- .remove = ak4104_spi_remove,
};
module_spi_driver(ak4104_spi_driver);
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
new file mode 100644
index 000000000000..23e868e4e3fb
--- /dev/null
+++ b/sound/soc/codecs/ak4118.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4118.c -- Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright 2018 DEVIALET
+ */
+
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#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" },
+ {}
+};
+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 <adrien.charruel@devialet.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c
new file mode 100644
index 000000000000..452559d8c97b
--- /dev/null
+++ b/sound/soc/codecs/ak4375.c
@@ -0,0 +1,606 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Based on code by Hu Jin
+ * Copyright (C) 2014 Asahi Kasei Microdevices Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* 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 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 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 = {
+ RUNTIME_PM_OPS(ak4375_runtime_suspend, ak4375_runtime_resume, NULL)
+ 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 = pm_ptr(&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 <vincent.knecht@mailoo.org>");
+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 000000000000..57cf601d3df3
--- /dev/null
+++ b/sound/soc/codecs/ak4458.c
@@ -0,0 +1,813 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK4458 DAC
+//
+// Copyright (C) 2016 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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 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)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak4458_rate_constraints);
+}
+
+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 (!IS_ERR_OR_NULL(ak4458->reset)) {
+ if (active)
+ reset_control_assert(ak4458->reset);
+ else
+ reset_control_deassert(ak4458->reset);
+ usleep_range(1000, 2000);
+ }
+}
+
+static int 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 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);
+}
+
+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 = {
+ RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL)
+ 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->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 = pm_ptr(&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 <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+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 000000000000..9ad869575f8d
--- /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 <linux/regmap.h>
+
+/* 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
index 9130d916f2f4..aadc46a47280 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4535.c -- AK4535 ALSA Soc Audio driver
*
@@ -6,10 +7,6 @@
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.c by Liam Girdwood
- *
- * 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.
*/
#include <linux/module.h>
@@ -251,8 +248,8 @@ static const struct snd_soc_dapm_route ak4535_audio_map[] = {
static int ak4535_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4535_priv *ak4535 = snd_soc_component_get_drvdata(component);
ak4535->sysclk = freq;
return 0;
@@ -262,9 +259,9 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak4535_priv *ak4535 = snd_soc_codec_get_drvdata(codec);
- u8 mode2 = snd_soc_read(codec, AK4535_MODE2) & ~(0x3 << 5);
+ 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)
@@ -283,14 +280,14 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
}
/* set rate */
- snd_soc_write(codec, AK4535_MODE2, mode2);
+ 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode1 = 0;
/* interface format */
@@ -308,40 +305,40 @@ static int ak4535_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* use 32 fs for BCLK to save power */
mode1 |= 0x4;
- snd_soc_write(codec, AK4535_MODE1, mode1);
+ snd_soc_component_write(component, AK4535_MODE1, mode1);
return 0;
}
-static int ak4535_mute(struct snd_soc_dai *dai, int mute)
+static int ak4535_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u16 mute_reg = snd_soc_read(codec, AK4535_DAC);
+ struct snd_soc_component *component = dai->component;
+ u16 mute_reg = snd_soc_component_read(component, AK4535_DAC);
+
if (!mute)
- snd_soc_write(codec, AK4535_DAC, mute_reg & ~0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg & ~0x20);
else
- snd_soc_write(codec, AK4535_DAC, mute_reg | 0x20);
+ snd_soc_component_write(component, AK4535_DAC, mute_reg | 0x20);
return 0;
}
-static int ak4535_set_bias_level(struct snd_soc_codec *codec,
+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_update_bits(codec, AK4535_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, AK4535_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4535_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, AK4535_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4535_PM2, 0x80, 0);
+ 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_update_bits(codec, AK4535_PM1, 0x80, 0);
+ snd_soc_component_update_bits(component, AK4535_PM1, 0x80, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -352,8 +349,9 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
- .digital_mute = ak4535_mute,
+ .mute_stream = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4535_dai = {
@@ -373,9 +371,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
.ops = &ak4535_dai_ops,
};
-static int ak4535_resume(struct snd_soc_codec *codec)
+static int ak4535_resume(struct snd_soc_component *component)
{
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -391,21 +389,22 @@ static const struct regmap_config ak4535_regmap = {
.num_reg_defaults = ARRAY_SIZE(ak4535_reg_defaults),
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
- .resume = ak4535_resume,
- .set_bias_level = ak4535_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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,
- const struct i2c_device_id *id)
+static int ak4535_i2c_probe(struct i2c_client *i2c)
{
struct ak4535_priv *ak4535;
int ret;
@@ -424,20 +423,14 @@ static int ak4535_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4535);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4535, &ak4535_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_ak4535, &ak4535_dai, 1);
return ret;
}
-static int ak4535_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4535_i2c_id[] = {
- { "ak4535", 0 },
+ { "ak4535" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
@@ -445,10 +438,8 @@ MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
static struct i2c_driver ak4535_i2c_driver = {
.driver = {
.name = "ak4535",
- .owner = THIS_MODULE,
},
- .probe = ak4535_i2c_probe,
- .remove = ak4535_i2c_remove,
+ .probe = ak4535_i2c_probe,
.id_table = ak4535_i2c_id,
};
diff --git a/sound/soc/codecs/ak4535.h b/sound/soc/codecs/ak4535.h
index 402de1d274bf..978caf52144f 100644
--- a/sound/soc/codecs/ak4535.h
+++ b/sound/soc/codecs/ak4535.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ak4535.h -- AK4535 Soc Audio driver
*
@@ -6,10 +7,6 @@
* Author: Richard Purdie <richard@openedhand.com>
*
* Based on wm8753.h
- *
- * 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 _AK4535_H
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
index 16ce9f9fefa1..b9607de5a191 100644
--- a/sound/soc/codecs/ak4554.c
+++ b/sound/soc/codecs/ak4554.c
@@ -1,13 +1,8 @@
-/*
- * ak4554.c
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+// ak4554.c
+//
+// Copyright (C) 2013 Renesas Solutions Corp.
+// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
#include <linux/module.h>
#include <sound/soc.h>
@@ -61,30 +56,27 @@ static struct snd_soc_dai_driver ak4554_dai = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_ak4554,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_ak4554,
&ak4554_dai, 1);
}
-static int ak4554_soc_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct of_device_id ak4554_of_match[] = {
+static const struct of_device_id ak4554_of_match[] = {
{ .compatible = "asahi-kasei,ak4554" },
{},
};
@@ -96,10 +88,9 @@ static struct platform_driver ak4554_driver = {
.of_match_table = ak4554_of_match,
},
.probe = ak4554_soc_probe,
- .remove = ak4554_soc_remove,
};
module_platform_driver(ak4554_driver);
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SoC AK4554 driver");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644
index 000000000000..de9e43185555
--- /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 <kuninori.morimoto.gx@renesas.com>
+//
+// 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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#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 const 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_property_read_bool(np, prop))
+ 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_property_read_bool(np, prop))
+ 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 <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c
new file mode 100644
index 000000000000..8f2442482f72
--- /dev/null
+++ b/sound/soc/codecs/ak4619.c
@@ -0,0 +1,912 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ak4619.c -- Asahi Kasei ALSA SoC Audio driver
+ *
+ * Copyright (C) 2023 Renesas Electronics Corporation
+ * Khanh Le <khanh.le.xr@renesas.com>
+ *
+ * Based on ak4613.c by Kuninori Morimoto
+ * Based on da7213.c by Adam Thomson
+ * Based on ak4641.c by Harald Welte
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+/*
+ * Registers
+ */
+
+#define PWR_MGMT 0x00 /* Power Management */
+#define AU_IFF1 0x01 /* Audio I/F Format */
+#define AU_IFF2 0x02 /* Audio I/F Format (Extended) */
+#define SYS_CLK 0x03 /* System Clock Setting */
+#define MIC_AMP1 0x04 /* MIC AMP Gain 1 */
+#define MIC_AMP2 0x05 /* MIC AMP Gain 2 */
+#define LADC1 0x06 /* ADC1 Lch Digital Volume */
+#define RADC1 0x07 /* ADC1 Rch Digital Volume */
+#define LADC2 0x08 /* ADC2 Lch Digital Volume */
+#define RADC2 0x09 /* ADC2 Rch Digital Volume */
+#define ADC_DF 0x0a /* ADC Digital Filter Setting */
+#define ADC_AI 0x0b /* ADC Analog Input Setting */
+#define ADC_MHPF 0x0D /* ADC Mute & HPF Control */
+#define LDAC1 0x0E /* DAC1 Lch Digital Volume */
+#define RDAC1 0x0F /* DAC1 Rch Digital Volume */
+#define LDAC2 0x10 /* DAC2 Lch Digital Volume */
+#define RDAC2 0x11 /* DAC2 Rch Digital Volume */
+#define DAC_IS 0x12 /* DAC Input Select Setting */
+#define DAC_DEMP 0x13 /* DAC De-Emphasis Setting */
+#define DAC_MF 0x14 /* DAC Mute & Filter Setting */
+
+/*
+ * Bit fields
+ */
+
+/* Power Management */
+#define PMAD2 BIT(5)
+#define PMAD1 BIT(4)
+#define PMDA2 BIT(2)
+#define PMDA1 BIT(1)
+#define RSTN BIT(0)
+
+/* Audio_I/F Format */
+#define DCF_STEREO_I2S (0x0 << 4)
+#define DCF_STEREO_MSB (0x5 << 4)
+#define DCF_PCM_SF (0x6 << 4)
+#define DCF_PCM_LF (0x7 << 4)
+#define DSL_32 (0x3 << 2)
+#define DCF_MASK (0x7 << 4)
+#define DSL_MASK (0x3 << 2)
+#define BCKP BIT(1)
+
+/* Audio_I/F Format (Extended) */
+#define DIDL_24 (0x0 << 2)
+#define DIDL_20 (0x1 << 2)
+#define DIDL_16 (0x2 << 2)
+#define DIDL_32 (0x3 << 2)
+#define DODL_24 (0x0 << 0)
+#define DODL_20 (0x1 << 0)
+#define DODL_16 (0x2 << 0)
+#define DIDL_MASK (0x3 << 2)
+#define DODL_MASK (0x3 << 0)
+#define SLOT BIT(4)
+
+/* System Clock Setting */
+#define FS_MASK 0x7
+
+/* MIC AMP Gain */
+#define MGNL_SHIFT 4
+#define MGNR_SHIFT 0
+#define MGN_MAX 0xB
+
+/* ADC Digital Volume */
+#define VOLAD_SHIFT 0
+#define VOLAD_MAX 0xFF
+
+/* ADC Digital Filter Setting */
+#define AD1SL_SHIFT 0
+#define AD2SL_SHIFT 4
+
+/* Analog Input Select */
+#define AD1LSEL_SHIFT 6
+#define AD1RSEL_SHIFT 4
+#define AD2LSEL_SHIFT 2
+#define AD2RSEL_SHIFT 0
+
+/* ADC Mute & HPF Control */
+#define ATSPAD_SHIFT 7
+#define AD1MUTE_SHIFT 5
+#define AD2MUTE_SHIFT 6
+#define AD1MUTE_MAX 1
+#define AD2MUTE_MAX 1
+#define AD1MUTE_EN BIT(5)
+#define AD2MUTE_EN BIT(6)
+#define AD1HPFN_SHIFT 1
+#define AD1HPFN_MAX 1
+#define AD2HPFN_SHIFT 2
+#define AD2HPFN_MAX 1
+
+/* DAC Digital Volume */
+#define VOLDA_SHIFT 0
+#define VOLDA_MAX 0xFF
+
+/* DAC Input Select Setting */
+#define DAC1SEL_SHIFT 0
+#define DAC2SEL_SHIFT 2
+
+/* DAC De-Emphasis Setting */
+#define DEM1_32000 (0x3 << 0)
+#define DEM1_44100 (0x0 << 0)
+#define DEM1_48000 (0x2 << 0)
+#define DEM1_OFF (0x1 << 0)
+#define DEM2_32000 (0x3 << 2)
+#define DEM2_44100 (0x0 << 2)
+#define DEM2_48000 (0x2 << 2)
+#define DEM2_OFF (0x1 << 2)
+#define DEM1_MASK (0x3 << 0)
+#define DEM2_MASK (0x3 << 2)
+#define DEM1_SHIFT 0
+#define DEM2_SHIFT 2
+
+/* DAC Mute & Filter Setting */
+#define DA1MUTE_SHIFT 4
+#define DA1MUTE_MAX 1
+#define DA2MUTE_SHIFT 5
+#define DA2MUTE_MAX 1
+#define DA1MUTE_EN BIT(4)
+#define DA2MUTE_EN BIT(5)
+#define ATSPDA_SHIFT 7
+#define DA1SL_SHIFT 0
+#define DA2SL_SHIFT 2
+
+/* Codec private data */
+struct ak4619_priv {
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list constraint;
+ int deemph_en;
+ unsigned int playback_rate;
+ unsigned int sysclk;
+};
+
+/*
+ * DAC Volume
+ *
+ * max : 0x00 : +12.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -115.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -11550, 50, 1);
+
+/*
+ * MIC Volume
+ *
+ * max : 0x0B : +27.0 dB
+ * ( 3 dB step )
+ * min: 0x00 : -6.0 dB
+ */
+static const DECLARE_TLV_DB_SCALE(mic_tlv, -600, 300, 0);
+
+/*
+ * ADC Volume
+ *
+ * max : 0x00 : +24.0 dB
+ * ( 0.5 dB step )
+ * min : 0xFE : -103.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+/* ADC & DAC Volume Level Transition Time select */
+static const char * const ak4619_vol_trans_txt[] = {
+ "4/fs", "16/fs"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_vol_trans, ADC_MHPF, ATSPAD_SHIFT, ak4619_vol_trans_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_vol_trans, DAC_MF, ATSPDA_SHIFT, ak4619_vol_trans_txt);
+
+/* ADC Digital Filter select */
+static const char * const ak4619_adc_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter",
+ "Voice Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_1_digi_fil, ADC_DF, AD1SL_SHIFT, ak4619_adc_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_adc_2_digi_fil, ADC_DF, AD2SL_SHIFT, ak4619_adc_digi_fil_txt);
+
+/* DAC De-Emphasis Filter select */
+static const char * const ak4619_dac_de_emp_txt[] = {
+ "44.1kHz", "OFF", "48kHz", "32kHz"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_de_emp, DAC_DEMP, DEM1_SHIFT, ak4619_dac_de_emp_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_de_emp, DAC_DEMP, DEM2_SHIFT, ak4619_dac_de_emp_txt);
+
+/* DAC Digital Filter select */
+static const char * const ak4619_dac_digi_fil_txt[] = {
+ "Sharp Roll-Off Filter",
+ "Slow Roll-Off Filter",
+ "Short Delay Sharp Roll-Off Filter",
+ "Short Delay Slow Roll-Off Filter"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_digi_fil, DAC_MF, DA1SL_SHIFT, ak4619_dac_digi_fil_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_digi_fil, DAC_MF, DA2SL_SHIFT, ak4619_dac_digi_fil_txt);
+
+/*
+ * Control functions
+ */
+
+static void ak4619_set_deemph(struct snd_soc_component *component)
+{
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ u8 dem = 0;
+
+ if (!ak4619->deemph_en)
+ return;
+
+ switch (ak4619->playback_rate) {
+ case 32000:
+ dem |= DEM1_32000 | DEM2_32000;
+ break;
+ case 44100:
+ dem |= DEM1_44100 | DEM2_44100;
+ break;
+ case 48000:
+ dem |= DEM1_48000 | DEM2_48000;
+ break;
+ default:
+ dem |= DEM1_OFF | DEM2_OFF;
+ break;
+ }
+ snd_soc_component_update_bits(component, DAC_DEMP, DEM1_MASK | DEM2_MASK, dem);
+}
+
+static int ak4619_put_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ int deemph_en = ucontrol->value.integer.value[0];
+ int ret = 0;
+
+ switch (deemph_en) {
+ case 0:
+ case 1:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ak4619->deemph_en != deemph_en)
+ ret = 1; /* The value changed */
+
+ ak4619->deemph_en = deemph_en;
+ ak4619_set_deemph(component);
+
+ return ret;
+}
+
+static int ak4619_get_deemph(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = ak4619->deemph_en;
+
+ return 0;
+};
+
+/*
+ * KControls
+ */
+static const struct snd_kcontrol_new ak4619_snd_controls[] = {
+
+ /* Volume controls */
+ SOC_DOUBLE_R_TLV("DAC 1 Volume", LDAC1, RDAC1, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("DAC 2 Volume", LDAC2, RDAC2, VOLDA_SHIFT, VOLDA_MAX, 1, dac_tlv),
+ SOC_DOUBLE_R_TLV("ADC 1 Volume", LADC1, RADC1, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+ SOC_DOUBLE_R_TLV("ADC 2 Volume", LADC2, RADC2, VOLAD_SHIFT, VOLAD_MAX, 1, adc_tlv),
+
+ SOC_DOUBLE_TLV("Mic 1 Volume", MIC_AMP1, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+ SOC_DOUBLE_TLV("Mic 2 Volume", MIC_AMP2, MGNL_SHIFT, MGNR_SHIFT, MGN_MAX, 0, mic_tlv),
+
+ /* Volume Level Transition Time controls */
+ SOC_ENUM("ADC Volume Level Transition Time", ak4619_adc_vol_trans),
+ SOC_ENUM("DAC Volume Level Transition Time", ak4619_dac_vol_trans),
+
+ /* Mute controls */
+ SOC_SINGLE("DAC 1 Switch", DAC_MF, DA1MUTE_SHIFT, DA1MUTE_MAX, 1),
+ SOC_SINGLE("DAC 2 Switch", DAC_MF, DA2MUTE_SHIFT, DA2MUTE_MAX, 1),
+
+ SOC_SINGLE("ADC 1 Switch", ADC_MHPF, AD1MUTE_SHIFT, AD1MUTE_MAX, 1),
+ SOC_SINGLE("ADC 2 Switch", ADC_MHPF, AD2MUTE_SHIFT, AD2MUTE_MAX, 1),
+
+ /* Filter controls */
+ SOC_ENUM("ADC 1 Digital Filter", ak4619_adc_1_digi_fil),
+ SOC_ENUM("ADC 2 Digital Filter", ak4619_adc_2_digi_fil),
+
+ SOC_SINGLE("ADC 1 HPF", ADC_MHPF, AD1HPFN_SHIFT, AD1HPFN_MAX, 1),
+ SOC_SINGLE("ADC 2 HPF", ADC_MHPF, AD2HPFN_SHIFT, AD2HPFN_MAX, 1),
+
+ SOC_ENUM("DAC 1 De-Emphasis Filter", ak4619_dac_1_de_emp),
+ SOC_ENUM("DAC 2 De-Emphasis Filter", ak4619_dac_2_de_emp),
+
+ SOC_ENUM("DAC 1 Digital Filter", ak4619_dac_1_digi_fil),
+ SOC_ENUM("DAC 2 Digital Filter", ak4619_dac_2_digi_fil),
+
+ SOC_SINGLE_BOOL_EXT("Playback De-Emphasis Switch", 0, ak4619_get_deemph, ak4619_put_deemph),
+};
+
+/*
+ * DAPM
+ */
+
+/* Analog input mode */
+static const char * const ak4619_analog_in_txt[] = {
+ "Differential", "Single-Ended1", "Single-Ended2", "Pseudo Differential"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_left_in, ADC_AI, AD1LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_1_right_in, ADC_AI, AD1RSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_left_in, ADC_AI, AD2LSEL_SHIFT, ak4619_analog_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_ad_2_right_in, ADC_AI, AD2RSEL_SHIFT, ak4619_analog_in_txt);
+
+static const struct snd_kcontrol_new ak4619_ad_1_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Left MUX", ak4619_ad_1_left_in);
+static const struct snd_kcontrol_new ak4619_ad_1_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 1 Right MUX", ak4619_ad_1_right_in);
+static const struct snd_kcontrol_new ak4619_ad_2_left_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Left MUX", ak4619_ad_2_left_in);
+static const struct snd_kcontrol_new ak4619_ad_2_right_in_mux =
+ SOC_DAPM_ENUM("Analog Input 2 Right MUX", ak4619_ad_2_right_in);
+
+/* DAC source mux */
+static const char * const ak4619_dac_in_txt[] = {
+ "SDIN1", "SDIN2", "SDOUT1", "SDOUT2"
+};
+
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_1_in, DAC_IS, DAC1SEL_SHIFT, ak4619_dac_in_txt);
+static SOC_ENUM_SINGLE_DECL(ak4619_dac_2_in, DAC_IS, DAC2SEL_SHIFT, ak4619_dac_in_txt);
+
+static const struct snd_kcontrol_new ak4619_dac_1_in_mux =
+ SOC_DAPM_ENUM("DAC 1 Source MUX", ak4619_dac_1_in);
+static const struct snd_kcontrol_new ak4619_dac_2_in_mux =
+ SOC_DAPM_ENUM("DAC 2 Source MUX", ak4619_dac_2_in);
+
+static const struct snd_soc_dapm_widget ak4619_dapm_widgets[] = {
+
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC1", NULL, PWR_MGMT, 1, 0),
+ SND_SOC_DAPM_DAC("DAC2", NULL, PWR_MGMT, 2, 0),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC1", NULL, PWR_MGMT, 4, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL, PWR_MGMT, 5, 0),
+
+ /* Outputs */
+ SND_SOC_DAPM_OUTPUT("AOUT1L"),
+ SND_SOC_DAPM_OUTPUT("AOUT2L"),
+
+ SND_SOC_DAPM_OUTPUT("AOUT1R"),
+ SND_SOC_DAPM_OUTPUT("AOUT2R"),
+
+ /* Inputs */
+ SND_SOC_DAPM_INPUT("AIN1L"),
+ SND_SOC_DAPM_INPUT("AIN2L"),
+ SND_SOC_DAPM_INPUT("AIN4L"),
+ SND_SOC_DAPM_INPUT("AIN5L"),
+
+ SND_SOC_DAPM_INPUT("AIN1R"),
+ SND_SOC_DAPM_INPUT("AIN2R"),
+ SND_SOC_DAPM_INPUT("AIN4R"),
+ SND_SOC_DAPM_INPUT("AIN5R"),
+
+ SND_SOC_DAPM_INPUT("MIC1L"),
+ SND_SOC_DAPM_INPUT("MIC1R"),
+ SND_SOC_DAPM_INPUT("MIC2L"),
+ SND_SOC_DAPM_INPUT("MIC2R"),
+
+ /* DAI */
+ SND_SOC_DAPM_AIF_IN("SDIN1", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SDOUT2", "Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* MUXs for Mic PGA source selection */
+ SND_SOC_DAPM_MUX("Analog Input 1 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 1 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_1_right_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Left MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_left_in_mux),
+ SND_SOC_DAPM_MUX("Analog Input 2 Right MUX", SND_SOC_NOPM, 0, 0, &ak4619_ad_2_right_in_mux),
+
+ /* MUXs for DAC source selection */
+ SND_SOC_DAPM_MUX("DAC 1 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_1_in_mux),
+ SND_SOC_DAPM_MUX("DAC 2 Source MUX", SND_SOC_NOPM, 0, 0, &ak4619_dac_2_in_mux),
+};
+
+static const struct snd_soc_dapm_route ak4619_intercon[] = {
+ /* Dest Connecting Widget Source */
+
+ /* Output path */
+ {"AOUT1L", NULL, "DAC1"},
+ {"AOUT2L", NULL, "DAC2"},
+
+ {"AOUT1R", NULL, "DAC1"},
+ {"AOUT2R", NULL, "DAC2"},
+
+ {"DAC1", NULL, "DAC 1 Source MUX"},
+ {"DAC2", NULL, "DAC 2 Source MUX"},
+
+ {"DAC 1 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 1 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 1 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 1 Source MUX", "SDOUT2", "SDOUT2"},
+
+ {"DAC 2 Source MUX", "SDIN1", "SDIN1"},
+ {"DAC 2 Source MUX", "SDIN2", "SDIN2"},
+ {"DAC 2 Source MUX", "SDOUT1", "SDOUT1"},
+ {"DAC 2 Source MUX", "SDOUT2", "SDOUT2"},
+
+ /* Input path */
+ {"SDOUT1", NULL, "ADC1"},
+ {"SDOUT2", NULL, "ADC2"},
+
+ {"ADC1", NULL, "Analog Input 1 Left MUX"},
+ {"ADC1", NULL, "Analog Input 1 Right MUX"},
+
+ {"ADC2", NULL, "Analog Input 2 Left MUX"},
+ {"ADC2", NULL, "Analog Input 2 Right MUX"},
+
+ {"Analog Input 1 Left MUX", "Differential", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended1", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Single-Ended2", "MIC1L"},
+ {"Analog Input 1 Left MUX", "Pseudo Differential", "MIC1L"},
+
+ {"Analog Input 1 Right MUX", "Differential", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended1", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Single-Ended2", "MIC1R"},
+ {"Analog Input 1 Right MUX", "Pseudo Differential", "MIC1R"},
+
+ {"Analog Input 2 Left MUX", "Differential", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended1", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Single-Ended2", "MIC2L"},
+ {"Analog Input 2 Left MUX", "Pseudo Differential", "MIC2L"},
+
+ {"Analog Input 2 Right MUX", "Differential", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended1", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Single-Ended2", "MIC2R"},
+ {"Analog Input 2 Right MUX", "Pseudo Differential", "MIC2R"},
+
+ {"MIC1L", NULL, "AIN1L"},
+ {"MIC1L", NULL, "AIN2L"},
+
+ {"MIC1R", NULL, "AIN1R"},
+ {"MIC1R", NULL, "AIN2R"},
+
+ {"MIC2L", NULL, "AIN4L"},
+ {"MIC2L", NULL, "AIN5L"},
+
+ {"MIC2R", NULL, "AIN4R"},
+ {"MIC2R", NULL, "AIN5R"},
+};
+
+static const struct reg_default ak4619_reg_defaults[] = {
+ { PWR_MGMT, 0x00 },
+ { AU_IFF1, 0x0C },
+ { AU_IFF2, 0x0C },
+ { SYS_CLK, 0x00 },
+ { MIC_AMP1, 0x22 },
+ { MIC_AMP2, 0x22 },
+ { LADC1, 0x30 },
+ { RADC1, 0x30 },
+ { LADC2, 0x30 },
+ { RADC2, 0x30 },
+ { ADC_DF, 0x00 },
+ { ADC_AI, 0x00 },
+ { ADC_MHPF, 0x00 },
+ { LDAC1, 0x18 },
+ { RDAC1, 0x18 },
+ { LDAC2, 0x18 },
+ { RDAC2, 0x18 },
+ { DAC_IS, 0x04 },
+ { DAC_DEMP, 0x05 },
+ { DAC_MF, 0x0A },
+};
+
+static int ak4619_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ u8 pwr_ctrl = 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ pwr_ctrl |= RSTN;
+ fallthrough;
+ case SND_SOC_BIAS_PREPARE:
+ pwr_ctrl |= PMAD1 | PMAD2 | PMDA1 | PMDA2;
+ fallthrough;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_OFF:
+ default:
+ break;
+ }
+
+ snd_soc_component_write(component, PWR_MGMT, pwr_ctrl);
+
+ return 0;
+}
+
+static int ak4619_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 ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+ unsigned int width;
+ unsigned int rate;
+ unsigned int fs;
+ bool is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u8 dai_ctrl = 0;
+ u8 clk_mode = 0;
+
+ width = params_width(params);
+ switch (width) {
+ case 16:
+ dai_ctrl |= is_play ? DIDL_16 : DODL_16;
+ break;
+ case 20:
+ dai_ctrl |= is_play ? DIDL_20 : DODL_20;
+ break;
+ case 24:
+ dai_ctrl |= is_play ? DIDL_24 : DODL_24;
+ break;
+ case 32:
+ if (is_play)
+ dai_ctrl |= DIDL_32;
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rate = params_rate(params);
+ if (rate)
+ fs = ak4619->sysclk / rate;
+ else
+ return -EINVAL;
+
+ switch (rate) {
+ case 8000:
+ case 11025:
+ case 12000:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ switch (fs) {
+ case 256:
+ clk_mode |= (0x0 << 0);
+ break;
+ case 384:
+ clk_mode |= (0x2 << 0);
+ break;
+ case 512:
+ clk_mode |= (0x3 << 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 64000:
+ case 88200:
+ case 96000:
+ if (fs == 256)
+ clk_mode |= (0x1 << 0);
+ else
+ return -EINVAL;
+ break;
+ case 176400:
+ case 192000:
+ if (fs == 128)
+ clk_mode |= (0x4 << 0);
+ else
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, SYS_CLK, FS_MASK, clk_mode);
+ snd_soc_component_update_bits(component, AU_IFF2,
+ is_play ? DIDL_MASK : DODL_MASK, dai_ctrl);
+
+ if (is_play) {
+ ak4619->playback_rate = rate;
+ ak4619_set_deemph(component);
+ }
+
+ return 0;
+}
+
+static int ak4619_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ u8 dai_fmt1 = 0;
+ u8 dai_fmt2 = 0;
+
+ /* Set clock normal/inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ dai_fmt1 |= BCKP;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_IF:
+ default:
+ return -EINVAL;
+ }
+
+ /* Only Stereo modes are supported */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ dai_fmt1 |= DCF_STEREO_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ dai_fmt1 |= DCF_STEREO_MSB;
+ break;
+ case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+ dai_fmt1 |= DCF_PCM_SF;
+ dai_fmt2 |= SLOT;
+ break;
+ case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+ dai_fmt1 |= DCF_PCM_LF;
+ dai_fmt2 |= SLOT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Only slave mode is support */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* By default only 64 BICK per LRCLK is supported */
+ dai_fmt1 |= DSL_32;
+
+ snd_soc_component_update_bits(component, AU_IFF1, DCF_MASK |
+ DSL_MASK | BCKP, dai_fmt1);
+ snd_soc_component_update_bits(component, AU_IFF2, SLOT, dai_fmt2);
+
+ return 0;
+}
+
+static int ak4619_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 ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619->sysclk = freq;
+
+ return 0;
+}
+
+static int ak4619_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+
+ snd_soc_component_update_bits(component, DAC_MF, DA1MUTE_EN, mute ? DA1MUTE_EN : 0);
+ snd_soc_component_update_bits(component, DAC_MF, DA2MUTE_EN, mute ? DA2MUTE_EN : 0);
+
+ return 0;
+}
+
+static void ak4619_hw_constraints(struct ak4619_priv *ak4619,
+ struct snd_pcm_runtime *runtime)
+{
+ struct snd_pcm_hw_constraint_list *constraint = &ak4619->constraint;
+ int ak4619_rate_mask = 0;
+ unsigned int fs;
+ int i;
+ static const unsigned int ak4619_sr[] = {
+ 8000,
+ 11025,
+ 12000,
+ 16000,
+ 22050,
+ 24000,
+ 32000,
+ 44100,
+ 48000,
+ 64000,
+ 88200,
+ 96000,
+ 176400,
+ 192000,
+ };
+
+ /*
+ * [8kHz - 48kHz] : 256fs, 384fs or 512fs
+ * [64kHz - 96kHz] : 256fs
+ * [176.4kHz, 192kHz] : 128fs
+ */
+
+ for (i = 0; i < ARRAY_SIZE(ak4619_sr); i++) {
+ fs = ak4619->sysclk / ak4619_sr[i];
+
+ switch (fs) {
+ case 512:
+ case 384:
+ case 256:
+ ak4619_rate_mask |= (1 << i);
+ break;
+ case 128:
+ switch (i) {
+ case (ARRAY_SIZE(ak4619_sr) - 1):
+ case (ARRAY_SIZE(ak4619_sr) - 2):
+ ak4619_rate_mask |= (1 << i);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ constraint->list = ak4619_sr;
+ constraint->mask = ak4619_rate_mask;
+ constraint->count = ARRAY_SIZE(ak4619_sr);
+
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, constraint);
+};
+
+#define PLAYBACK_MODE 0
+#define CAPTURE_MODE 1
+
+static int ak4619_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ak4619_priv *ak4619 = snd_soc_component_get_drvdata(component);
+
+ ak4619_hw_constraints(ak4619, substream->runtime);
+
+ return 0;
+}
+
+static u64 ak4619_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 */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops ak4619_dai_ops = {
+ .startup = ak4619_dai_startup,
+ .set_sysclk = ak4619_dai_set_sysclk,
+ .set_fmt = ak4619_dai_set_fmt,
+ .hw_params = ak4619_dai_hw_params,
+ .mute_stream = ak4619_dai_mute,
+ .auto_selectable_formats = ak4619_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats),
+};
+
+static const struct snd_soc_component_driver soc_component_dev_ak4619 = {
+ .set_bias_level = ak4619_set_bias_level,
+ .controls = ak4619_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4619_snd_controls),
+ .dapm_widgets = ak4619_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4619_dapm_widgets),
+ .dapm_routes = ak4619_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4619_intercon),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config ak4619_regmap_cfg = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x14,
+ .reg_defaults = ak4619_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ak4619_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static const struct of_device_id ak4619_of_match[] = {
+ { .compatible = "asahi-kasei,ak4619", .data = &ak4619_regmap_cfg },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4619_of_match);
+
+static const struct i2c_device_id ak4619_i2c_id[] = {
+ { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id);
+
+#define AK4619_RATES SNDRV_PCM_RATE_8000_192000
+
+#define AK4619_DAC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AK4619_ADC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S20_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4619_dai = {
+ .name = "ak4619-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_DAC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AK4619_RATES,
+ .formats = AK4619_ADC_FORMATS,
+ },
+ .ops = &ak4619_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int ak4619_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct ak4619_priv *ak4619;
+ int ret;
+
+ ak4619 = devm_kzalloc(dev, sizeof(*ak4619), GFP_KERNEL);
+ if (!ak4619)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, ak4619);
+
+ ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg);
+ if (IS_ERR(ak4619->regmap)) {
+ ret = PTR_ERR(ak4619->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(dev, &soc_component_dev_ak4619,
+ &ak4619_dai, 1);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register ak4619 component: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct i2c_driver ak4619_i2c_driver = {
+ .driver = {
+ .name = "ak4619-codec",
+ .of_match_table = ak4619_of_match,
+ },
+ .probe = ak4619_i2c_probe,
+ .id_table = ak4619_i2c_id,
+};
+module_i2c_driver(ak4619_i2c_driver);
+
+MODULE_DESCRIPTION("SoC AK4619 driver");
+MODULE_AUTHOR("Khanh Le <khanh.le.xr@renesas.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 70861c7b1631..ec33e7d73c6c 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ak4641.c -- AK4641 ALSA Soc Audio driver
*
@@ -5,10 +6,6 @@
* Copyright (C) 2011 Dmitry Artamonow <mad_soft@inbox.ru>
*
* Based on ak4535.c by Richard Purdie
- *
- * 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.
*/
#include <linux/module.h>
@@ -27,7 +24,27 @@
#include <sound/tlv.h>
#include <sound/ak4641.h>
-#include "ak4641.h"
+/* 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 {
@@ -50,9 +67,9 @@ static const struct reg_default ak4641_reg_defaults[] = {
static const int deemph_settings[] = {44100, 0, 48000, 32000};
-static int ak4641_set_deemph(struct snd_soc_codec *codec)
+static int ak4641_set_deemph(struct snd_soc_component *component)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
int i, best = 0;
for (i = 0 ; i < ARRAY_SIZE(deemph_settings); i++) {
@@ -66,33 +83,33 @@ static int ak4641_set_deemph(struct snd_soc_codec *codec)
best = i;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", best);
+ dev_dbg(component->dev, "Set deemphasis %d\n", best);
- return snd_soc_update_bits(codec, AK4641_DAC, 0x3, 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ 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(codec);
+ return ak4641_set_deemph(component);
}
static int ak4641_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = ak4641->deemph;
+ ucontrol->value.integer.value[0] = ak4641->deemph;
return 0;
};
@@ -287,8 +304,8 @@ static const struct snd_soc_dapm_route ak4641_audio_map[] = {
static int ak4641_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4641_priv *ak4641 = snd_soc_component_get_drvdata(component);
ak4641->sysclk = freq;
return 0;
@@ -298,8 +315,8 @@ 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_codec *codec = dai->codec;
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -320,16 +337,16 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
mode2 = (0x0 << 5);
break;
default:
- dev_err(codec->dev, "Error: unsupported fs=%d\n", fs);
+ dev_err(component->dev, "Error: unsupported fs=%d\n", fs);
return -EINVAL;
}
- snd_soc_update_bits(codec, AK4641_MODE2, (0x3 << 5), mode2);
+ 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(codec);
+ ak4641_set_deemph(component);
}
return 0;
@@ -338,7 +355,7 @@ static int ak4641_i2s_hw_params(struct snd_pcm_substream *substream,
static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 btif;
int ret;
@@ -360,7 +377,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- ret = snd_soc_update_bits(codec, AK4641_BTIF, (0x3 << 5), btif);
+ ret = snd_soc_component_update_bits(component, AK4641_BTIF, (0x3 << 5), btif);
if (ret < 0)
return ret;
@@ -370,7 +387,7 @@ static int ak4641_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode1 = 0;
/* interface format */
@@ -385,34 +402,34 @@ static int ak4641_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, AK4641_MODE1, mode1);
+ return snd_soc_component_write(component, AK4641_MODE1, mode1);
}
-static int ak4641_mute(struct snd_soc_dai *dai, int mute)
+static int ak4641_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, AK4641_DAC, 0x20, mute ? 0x20 : 0);
+ return snd_soc_component_update_bits(component, AK4641_DAC, 0x20, mute ? 0x20 : 0);
}
-static int ak4641_set_bias_level(struct snd_soc_codec *codec,
+static int ak4641_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ak4641_priv *ak4641 = snd_soc_codec_get_drvdata(codec);
- struct ak4641_platform_data *pdata = codec->dev->platform_data;
+ 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_update_bits(codec, AK4641_DAC, 0x20, 0);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* mute */
- snd_soc_update_bits(codec, AK4641_DAC, 0x20, 0x20);
+ snd_soc_component_update_bits(component, AK4641_DAC, 0x20, 0x20);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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);
@@ -422,16 +439,16 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
ret = regcache_sync(ak4641->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, AK4641_PM1, 0x80, 0x80);
- snd_soc_update_bits(codec, AK4641_PM2, 0x80, 0);
+ 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_update_bits(codec, AK4641_PM1, 0x80, 0);
+ 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))
@@ -439,7 +456,6 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
regcache_mark_dirty(ak4641->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -451,15 +467,17 @@ static int ak4641_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ak4641_i2s_dai_ops = {
.hw_params = ak4641_i2s_hw_params,
.set_fmt = ak4641_i2s_set_dai_fmt,
- .digital_mute = ak4641_mute,
+ .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,
- .digital_mute = ak4641_mute,
+ .mute_stream = ak4641_mute,
.set_sysclk = ak4641_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver ak4641_dai[] = {
@@ -481,7 +499,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_i2s_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "ak4641-voice",
@@ -501,11 +519,11 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
.formats = AK4641_FORMATS,
},
.ops = &ak4641_pcm_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
+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,
@@ -513,7 +531,10 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
- .suspend_bias_off = true,
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
};
static const struct regmap_config ak4641_regmap = {
@@ -526,8 +547,7 @@ static const struct regmap_config ak4641_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4641_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ak4641_i2c_probe(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
struct ak4641_priv *ak4641;
@@ -562,7 +582,8 @@ static int ak4641_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, ak4641);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_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;
@@ -583,12 +604,10 @@ err_out:
return ret;
}
-static int ak4641_i2c_remove(struct i2c_client *i2c)
+static void ak4641_i2c_remove(struct i2c_client *i2c)
{
struct ak4641_platform_data *pdata = i2c->dev.platform_data;
- snd_soc_unregister_codec(&i2c->dev);
-
if (pdata) {
if (gpio_is_valid(pdata->gpio_power)) {
gpio_set_value(pdata->gpio_power, 0);
@@ -597,12 +616,10 @@ static int ak4641_i2c_remove(struct i2c_client *i2c)
if (gpio_is_valid(pdata->gpio_npdn))
gpio_free(pdata->gpio_npdn);
}
-
- return 0;
}
static const struct i2c_device_id ak4641_i2c_id[] = {
- { "ak4641", 0 },
+ { "ak4641" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
@@ -610,7 +627,6 @@ MODULE_DEVICE_TABLE(i2c, ak4641_i2c_id);
static struct i2c_driver ak4641_i2c_driver = {
.driver = {
.name = "ak4641",
- .owner = THIS_MODULE,
},
.probe = ak4641_i2c_probe,
.remove = ak4641_i2c_remove,
diff --git a/sound/soc/codecs/ak4641.h b/sound/soc/codecs/ak4641.h
deleted file mode 100644
index 4a263248efea..000000000000
--- a/sound/soc/codecs/ak4641.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * ak4641.h -- AK4641 SoC Audio driver
- *
- * Copyright 2008 Harald Welte <laforge@gnufiish.org>
- *
- * Based on ak4535.h
- *
- * 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 _AK4641_H
-#define _AK4641_H
-
-/* 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
-
-#define AK4641_CACHEREGNUM 0x14
-
-
-
-#define AK4641_DAI_HIFI 0
-#define AK4641_DAI_VOICE 1
-
-
-#endif
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index dde8b49c19ad..fe035d2fc913 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -1,17 +1,13 @@
-/*
- * ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Based on wm8731.c by Richard Purdie
- * Based on ak4535.c by Richard Purdie
- * Based on wm8753.c by Liam Girdwood
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// ak4642.c -- AK4642/AK4643 ALSA Soc Audio driver
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Based on wm8731.c by Richard Purdie
+// Based on ak4535.c by Richard Purdie
+// Based on wm8753.c by Liam Girdwood
/* ** CAUTION **
*
@@ -23,10 +19,12 @@
* AK4648 is tested.
*/
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -64,12 +62,15 @@
#define FIL1_0 0x1c
#define FIL1_1 0x1d
#define FIL1_2 0x1e
-#define FIL1_3 0x1f
+#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
+#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 */
@@ -97,6 +98,9 @@
#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))
@@ -122,11 +126,8 @@
#define I2S (3 << 0)
/* MD_CTL2 */
-#define FS0 (1 << 0)
-#define FS1 (1 << 1)
-#define FS2 (1 << 2)
-#define FS3 (1 << 5)
-#define FS_MASK (FS0 | FS1 | FS2 | FS3)
+#define FSs(val) (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val) ((val & 0x3) << 6)
/* MD_CTL3 */
#define BST1 (1 << 3)
@@ -141,6 +142,7 @@ struct ak4642_drvdata {
struct ak4642_priv {
const struct ak4642_drvdata *drvdata;
+ struct clk *mcko;
};
/*
@@ -168,6 +170,29 @@ 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 */
@@ -182,12 +207,15 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = {
SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0),
- SND_SOC_DAPM_MIXER("LINEOUT Mixer", PW_MGMT1, 3, 0,
+ SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0,
&ak4642_lout_mixer_controls[0],
- ARRAY_SIZE(ak4642_lout_mixer_controls)),
+ 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", "HiFi Playback", PW_MGMT1, 2, 0),
+ SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0),
};
static const struct snd_soc_dapm_route ak4642_intercon[] = {
@@ -205,12 +233,14 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
{"DACH", NULL, "DAC"},
{"LINEOUT Mixer", "DACL", "DAC"},
+
+ { "DAC", NULL, "Playback" },
};
/*
* ak4642 register cache
*/
-static const struct reg_default ak4642_reg[] = {
+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 },
@@ -223,6 +253,14 @@ static const struct reg_default ak4642_reg[] = {
{ 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 },
@@ -240,7 +278,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
/*
@@ -253,8 +291,8 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p97.
*/
- snd_soc_write(codec, L_IVC, 0x91); /* volume */
- snd_soc_write(codec, R_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, L_IVC, 0x91); /* volume */
+ snd_soc_component_write(component, R_IVC, 0x91); /* volume */
} else {
/*
* start stereo input
@@ -269,11 +307,11 @@ static int ak4642_dai_startup(struct snd_pcm_substream *substream,
* This operation came from example code of
* "ASAHI KASEI AK4642" (japanese) manual p94.
*/
- snd_soc_update_bits(codec, SG_SL1, PMMP | MGAIN0, PMMP | MGAIN0);
- snd_soc_write(codec, TIMER, ZTM(0x3) | WTM(0x3));
- snd_soc_write(codec, ALC_CTL1, ALC | LMTH0);
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, PMADL);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, PMADR);
+ 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;
@@ -283,22 +321,22 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (is_play) {
} else {
/* stop stereo input */
- snd_soc_update_bits(codec, PW_MGMT1, PMADL, 0);
- snd_soc_update_bits(codec, PW_MGMT3, PMADR, 0);
- snd_soc_update_bits(codec, ALC_CTL1, ALC, 0);
+ 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_codec *codec = codec_dai->codec;
- struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
u8 pll;
int extended_freq = 0;
@@ -340,33 +378,33 @@ static int ak4642_dai_set_sysclk(struct snd_soc_dai *codec_dai,
if (extended_freq && !priv->drvdata->extended_frequencies)
return -EINVAL;
- snd_soc_update_bits(codec, MD_CTL1, PLL_MASK, pll);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 data;
u8 bcko;
data = MCKO | PMPLL; /* use MCKO */
bcko = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, PW_MGMT2, MS | MCKO | PMPLL, data);
- snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
+ 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;
@@ -383,7 +421,45 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+ 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;
}
@@ -392,66 +468,27 @@ 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_codec *codec = dai->codec;
- u8 rate;
+ struct snd_soc_component *component = dai->component;
+ struct ak4642_priv *priv = snd_soc_component_get_drvdata(component);
+ u32 rate = clk_get_rate(priv->mcko);
- switch (params_rate(params)) {
- case 7350:
- rate = FS2;
- break;
- case 8000:
- rate = 0;
- break;
- case 11025:
- rate = FS2 | FS0;
- break;
- case 12000:
- rate = FS0;
- break;
- case 14700:
- rate = FS2 | FS1;
- break;
- case 16000:
- rate = FS1;
- break;
- case 22050:
- rate = FS2 | FS1 | FS0;
- break;
- case 24000:
- rate = FS1 | FS0;
- break;
- case 29400:
- rate = FS3 | FS2 | FS1;
- break;
- case 32000:
- rate = FS3 | FS1;
- break;
- case 44100:
- rate = FS3 | FS2 | FS1 | FS0;
- break;
- case 48000:
- rate = FS3 | FS1 | FS0;
- break;
- default:
- return -EINVAL;
- }
- snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+ if (!rate)
+ rate = params_rate(params) * 256;
- return 0;
+ return ak4642_set_mcko(component, rate);
}
-static int ak4642_set_bias_level(struct snd_soc_codec *codec,
+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_write(codec, PW_MGMT1, 0x00);
+ snd_soc_component_write(component, PW_MGMT1, 0x00);
break;
default:
- snd_soc_update_bits(codec, PW_MGMT1, PMVCM, PMVCM);
+ snd_soc_component_update_bits(component, PW_MGMT1, PMVCM, PMVCM);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -468,30 +505,50 @@ static struct snd_soc_dai_driver ak4642_dai = {
.name = "ak4642-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE },
.ops = &ak4642_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ 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 struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+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,
@@ -500,22 +557,35 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.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 = ARRAY_SIZE(ak4642_reg) + 1,
+ .max_register = FIL1_3,
.reg_defaults = ak4642_reg,
- .num_reg_defaults = ARRAY_SIZE(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 = ARRAY_SIZE(ak4648_reg) + 1,
+ .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 = {
@@ -523,7 +593,7 @@ static const struct ak4642_drvdata ak4642_drvdata = {
};
static const struct ak4642_drvdata ak4643_drvdata = {
- .regmap_config = &ak4642_regmap,
+ .regmap_config = &ak4643_regmap,
};
static const struct ak4642_drvdata ak4648_drvdata = {
@@ -531,35 +601,57 @@ static const struct ak4642_drvdata ak4648_drvdata = {
.extended_frequencies = 1,
};
-static const struct of_device_id ak4642_of_match[];
-static int ak4642_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
{
- struct device_node *np = i2c->dev.of_node;
- const struct ak4642_drvdata *drvdata = NULL;
+ 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 int ak4642_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct ak4642_drvdata *drvdata;
struct regmap *regmap;
struct ak4642_priv *priv;
+ struct clk *mcko = NULL;
- if (np) {
- const struct of_device_id *of_id;
-
- of_id = of_match_device(ak4642_of_match, &i2c->dev);
- if (of_id)
- drvdata = of_id->data;
- } else {
- drvdata = (const struct ak4642_drvdata *)id->driver_data;
+ if (dev_fwnode(dev)) {
+ mcko = ak4642_of_parse_mcko(dev);
+ if (IS_ERR(mcko))
+ mcko = NULL;
}
- if (!drvdata) {
- dev_err(&i2c->dev, "Unknown device type\n");
- return -EINVAL;
- }
+ drvdata = i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(dev, -EINVAL, "Unknown device type\n");
- priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->drvdata = drvdata;
+ priv->mcko = mcko;
i2c_set_clientdata(i2c, priv);
@@ -567,21 +659,15 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ak4642, &ak4642_dai, 1);
-}
-
-static int ak4642_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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);
@@ -589,18 +675,16 @@ 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",
- .owner = THIS_MODULE,
.of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
- .remove = ak4642_i2c_remove,
.id_table = ak4642_i2c_id,
};
@@ -608,4 +692,4 @@ module_i2c_driver(ak4642_i2c_driver);
MODULE_DESCRIPTION("Soc AK4642 driver");
MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 632e89f793a7..d545aa2e0a39 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ak4671.c -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
@@ -163,15 +158,15 @@ static const struct snd_kcontrol_new ak4671_snd_controls[] = {
static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, AK4671_MUTEN);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, AK4671_LOUT2_POWER_MANAGERMENT,
+ snd_soc_component_update_bits(component, AK4671_LOUT2_POWER_MANAGERMENT,
AK4671_MUTEN, 0);
break;
}
@@ -343,25 +338,25 @@ static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
};
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"},
+ {"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", 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"},
+ {"LOUT2 Mix Amp", NULL, "LOUT2 Mixer"},
+ {"ROUT2 Mix Amp", NULL, "ROUT2 Mixer"},
{"LOUT3 Mixer", "DACSL", "DAC Left"},
{"ROUT3 Mixer", "DACSR", "DAC Right"},
@@ -381,18 +376,18 @@ static const struct snd_soc_dapm_route ak4671_intercon[] = {
{"LIN2", NULL, "Mic Bias"},
{"RIN2", NULL, "Mic Bias"},
- {"ADC Left", "NULL", "LIN MUX"},
- {"ADC Right", "NULL", "RIN MUX"},
+ {"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"},
+ {"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"},
@@ -427,10 +422,10 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 fs;
- fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ fs = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
fs &= ~AK4671_FS;
switch (params_rate(params)) {
@@ -465,7 +460,7 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+ snd_soc_component_write(component, AK4671_PLL_MODE_SELECT0, fs);
return 0;
}
@@ -473,10 +468,10 @@ static int ak4671_hw_params(struct snd_pcm_substream *substream,
static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 pll;
- pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+ pll = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT0);
pll &= ~AK4671_PLL;
switch (freq) {
@@ -511,25 +506,25 @@ static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
return -EINVAL;
}
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 mode;
u8 format;
/* set master/slave audio interface */
- mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+ mode = snd_soc_component_read(component, AK4671_PLL_MODE_SELECT1);
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode |= AK4671_M_S;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
mode &= ~(AK4671_M_S);
break;
default:
@@ -537,7 +532,7 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* interface format */
- format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+ format = snd_soc_component_read(component, AK4671_FORMAT_SELECT);
format &= ~AK4671_DIF;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -557,27 +552,26 @@ static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
/* set mode and format */
- snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
- snd_soc_write(codec, AK4671_FORMAT_SELECT, 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_codec *codec,
+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_update_bits(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+ snd_soc_component_update_bits(component, AK4671_AD_DA_POWER_MANAGEMENT,
AK4671_PMVCM, AK4671_PMVCM);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+ snd_soc_component_write(component, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -611,14 +605,17 @@ static struct snd_soc_dai_driver ak4671_dai = {
.ops = &ak4671_dai_ops,
};
-static struct snd_soc_codec_driver soc_codec_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),
+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 = {
@@ -631,8 +628,7 @@ static const struct regmap_config ak4671_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int ak4671_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ak4671_i2c_probe(struct i2c_client *client)
{
struct regmap *regmap;
int ret;
@@ -644,19 +640,13 @@ static int ak4671_i2c_probe(struct i2c_client *client,
return ret;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_ak4671, &ak4671_dai, 1);
+ ret = devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_ak4671, &ak4671_dai, 1);
return ret;
}
-static int ak4671_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ak4671_i2c_id[] = {
- { "ak4671", 0 },
+ { "ak4671" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
@@ -664,10 +654,8 @@ MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
static struct i2c_driver ak4671_i2c_driver = {
.driver = {
.name = "ak4671-codec",
- .owner = THIS_MODULE,
},
.probe = ak4671_i2c_probe,
- .remove = ak4671_i2c_remove,
.id_table = ak4671_i2c_id,
};
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
index 394a34d3f50a..3dac0a1ae772 100644
--- a/sound/soc/codecs/ak4671.h
+++ b/sound/soc/codecs/ak4671.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ak4671.h -- audio driver for AK4671
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _AK4671_H
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index afa95360826d..6525d50b7ab2 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -1,20 +1,18 @@
+// 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 <zonque@gmail.com>
- *
- * 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.
*/
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -24,7 +22,7 @@ static const char * const supply_names[] = {
};
struct ak5386_priv {
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@@ -38,30 +36,29 @@ static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
{ "Capture", NULL, "AINR" },
};
-static int ak5386_soc_probe(struct snd_soc_codec *codec)
+static int ak5386_soc_probe(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
-static int ak5386_soc_remove(struct snd_soc_codec *codec)
+static void ak5386_soc_remove(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
- return 0;
}
#ifdef CONFIG_PM
-static int ak5386_soc_suspend(struct snd_soc_codec *codec)
+static int ak5386_soc_suspend(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int ak5386_soc_resume(struct snd_soc_component *component)
{
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
}
#else
@@ -69,26 +66,29 @@ static int ak5386_soc_resume(struct snd_soc_codec *codec)
#define ak5386_soc_resume NULL
#endif /* CONFIG_PM */
-static struct snd_soc_codec_driver soc_codec_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),
+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_codec *codec = codec_dai->codec;
+ 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(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -99,8 +99,8 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
/*
* From the datasheet:
@@ -112,8 +112,7 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
* the AK5386 in power-down mode (PDN pin = “L”).
*/
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 1);
+ gpiod_set_value(priv->reset_gpio, 1);
return 0;
}
@@ -121,11 +120,10 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
static int ak5386_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
+ gpiod_set_value(priv->reset_gpio, 0);
return 0;
}
@@ -169,7 +167,6 @@ static int ak5386_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
- priv->reset_gpio = -EINVAL;
dev_set_drvdata(dev, priv);
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
@@ -180,29 +177,19 @@ static int ak5386_probe(struct platform_device *pdev)
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);
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get AK5386 reset GPIO\n");
- 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;
+ gpiod_set_consumer_name(priv->reset_gpio, "AK5386 Reset");
- return snd_soc_register_codec(dev, &soc_codec_ak5386,
+ return devm_snd_soc_register_component(dev, &soc_component_ak5386,
&ak5386_dai, 1);
}
-static int ak5386_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
static struct platform_driver ak5386_driver = {
.probe = ak5386_probe,
- .remove = ak5386_remove,
.driver = {
.name = "ak5386",
.of_match_table = of_match_ptr(ak5386_dt_ids),
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
new file mode 100644
index 000000000000..683f3e472f50
--- /dev/null
+++ b/sound/soc/codecs/ak5558.c
@@ -0,0 +1,508 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Audio driver for AK5558 ADC
+//
+// Copyright (C) 2015 Asahi Kasei Microdevices Corporation
+// Copyright 2018 NXP
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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 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 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 = {
+ RUNTIME_PM_OPS(ak5558_runtime_suspend, ak5558_runtime_resume, NULL)
+ 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 = pm_ptr(&ak5558_pm),
+ },
+ .probe = ak5558_i2c_probe,
+ .remove = ak5558_i2c_remove,
+};
+
+module_i2c_driver(ak5558_i2c_driver);
+
+MODULE_AUTHOR("Junichi Wakasugi <wakasugi.jb@om.asahi-kasei.co.jp>");
+MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
+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 000000000000..61059086f161
--- /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
index 0e357996864b..fbf723758079 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5623.c -- alc562[123] ALSA Soc Audio driver
*
@@ -6,13 +7,7 @@
*
* Copyright 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
- *
* Based on WM8753.c
- *
- * 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.
- *
*/
#include <linux/module.h>
@@ -47,28 +42,28 @@ struct alc5623_priv {
unsigned int jack_det_ctrl;
};
-static inline int alc5623_reset(struct snd_soc_codec *codec)
+static inline int alc5623_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, ALC5623_RESET, 0);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_write(codec, ALC5623_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5623_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5623_HID_CTRL_DATA, 0);
break;
}
@@ -82,12 +77,11 @@ static int amp_mixer_event(struct snd_soc_dapm_widget *w,
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 unsigned int boost_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+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),
-};
+ 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[] = {
@@ -527,7 +521,7 @@ 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -535,12 +529,12 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -ENODEV;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ 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_read(codec, ALC5623_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
if (reg & ALC5623_DAI_SDP_SLAVE_MODE)
return 0;
@@ -576,13 +570,13 @@ static int alc5623_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
if (!pll_div)
return -EINVAL;
- snd_soc_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
- snd_soc_write(codec, ALC5623_PLL_CTRL, pll_div);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD2,
+ 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_write(codec, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
+ snd_soc_component_write(component, ALC5623_GLOBAL_CLK_CTRL_REG, gbl_clk);
return 0;
}
@@ -605,9 +599,9 @@ static const struct _coeff_div coeff_div[] = {
{384*1, 0x0c6b},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -623,8 +617,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 8192000:
@@ -644,15 +638,15 @@ static int alc5623_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ 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:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5623_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -696,18 +690,18 @@ static int alc5623_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ 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_codec *codec = dai->codec;
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, ALC5623_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5623_DAI_CONTROL);
iface &= ~ALC5623_DAI_I2S_DL_MASK;
/* bit size */
@@ -729,30 +723,30 @@ static int alc5623_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5623_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5623_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- dev_dbg(codec->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
+ dev_dbg(component->dev, "%s: sysclk=%d,rate=%d,coeff=0x%04x\n",
__func__, alc5623->sysclk, rate, coeff);
- snd_soc_write(codec, ALC5623_STEREO_AD_DA_CLK_CTRL, 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)
+static int alc5623_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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_read(codec, ALC5623_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5623_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5623_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5623_MISC_CTRL, mute_reg);
}
#define ALC5623_ADD2_POWER_EN (ALC5623_PWR_ADD2_VREF \
@@ -770,63 +764,62 @@ static int alc5623_mute(struct snd_soc_dai *dai, int mute)
(ALC5623_PWR_ADD1_SHORT_CURR_DET_EN \
| ALC5623_PWR_ADD1_HP_OUT_AMP)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
- snd_soc_update_bits(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_PWR_ADD1_SOFTGEN_EN,
ALC5623_PWR_ADD1_SOFTGEN_EN);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD3, ALC5623_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ 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_write(codec, ALC5623_PWR_MANAG_ADD2, ALC5623_ADD2_POWER_EN);
+ 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_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN_5622);
else
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD1,
ALC5623_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5623_MISC_CTRL,
+ 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_codec *codec,
+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(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD2,
+ snd_soc_component_write(component, ALC5623_PWR_MANAG_ADD2,
ALC5623_PWR_ADD2_VREF);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3,
+ 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_write(codec, ALC5623_PWR_MANAG_ADD2, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD3, 0);
- snd_soc_write(codec, ALC5623_PWR_MANAG_ADD1, 0);
+ 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;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -836,10 +829,11 @@ static int alc5623_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5623_dai_ops = {
.hw_params = alc5623_pcm_hw_params,
- .digital_mute = alc5623_mute,
+ .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 = {
@@ -864,25 +858,25 @@ static struct snd_soc_dai_driver alc5623_dai = {
.ops = &alc5623_dai_ops,
};
-static int alc5623_suspend(struct snd_soc_codec *codec)
+static int alc5623_suspend(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int alc5623_resume(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to sync register cache: %d\n",
+ dev_err(component->dev, "Failed to sync register cache: %d\n",
ret);
regcache_cache_only(alc5623->regmap, true);
return ret;
@@ -891,41 +885,41 @@ static int alc5623_resume(struct snd_soc_codec *codec)
return 0;
}
-static int alc5623_probe(struct snd_soc_codec *codec)
+static int alc5623_probe(struct snd_soc_component *component)
{
- struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct alc5623_priv *alc5623 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
- alc5623_reset(codec);
+ alc5623_reset(component);
if (alc5623->add_ctrl) {
- snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
+ snd_soc_component_write(component, ALC5623_ADD_CTRL_REG,
alc5623->add_ctrl);
}
if (alc5623->jack_det_ctrl) {
- snd_soc_write(codec, 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_codec_controls(codec, alc5621_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5621_vol_snd_controls,
ARRAY_SIZE(alc5621_vol_snd_controls));
break;
case 0x22:
- snd_soc_add_codec_controls(codec, alc5622_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5622_vol_snd_controls,
ARRAY_SIZE(alc5622_vol_snd_controls));
break;
case 0x23:
- snd_soc_add_codec_controls(codec, alc5623_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_vol_snd_controls,
ARRAY_SIZE(alc5623_vol_snd_controls));
break;
default:
return -EINVAL;
}
- snd_soc_add_codec_controls(codec, alc5623_snd_controls,
+ snd_soc_add_component_controls(component, alc5623_snd_controls,
ARRAY_SIZE(alc5623_snd_controls));
snd_soc_dapm_new_controls(dapm, alc5623_dapm_widgets,
@@ -953,12 +947,15 @@ static int alc5623_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
- .probe = alc5623_probe,
- .suspend = alc5623_suspend,
- .resume = alc5623_resume,
- .set_bias_level = alc5623_set_bias_level,
- .suspend_bias_off = true,
+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 = {
@@ -970,19 +967,27 @@ static const struct regmap_config alc5623_regmap = {
.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,
- const struct i2c_device_id *id)
+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;
+ unsigned int matched_id;
int ret;
u32 val32;
@@ -1011,10 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client,
}
vid2 >>= 8;
- if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10ec) || (vid2 != matched_id)) {
dev_err(&client->dev, "unknown or wrong codec\n");
- dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
- 0x10ec, id->driver_data,
+ dev_err(&client->dev, "Expected %x:%x, got %x:%x\n",
+ 0x10ec, matched_id,
vid1, vid2);
return -ENODEV;
}
@@ -1054,43 +1061,29 @@ static int alc5623_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, alc5623);
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5623, &alc5623_dai, 1);
+ 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;
}
-static int alc5623_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5623_i2c_table[] = {
- {"alc5621", 0x21},
- {"alc5622", 0x22},
- {"alc5623", 0x23},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
-
+#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",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(alc5623_of_match),
},
.probe = alc5623_i2c_probe,
- .remove = alc5623_i2c_remove,
.id_table = alc5623_i2c_table,
};
diff --git a/sound/soc/codecs/alc5623.h b/sound/soc/codecs/alc5623.h
index f3d68260d425..1dd88c772509 100644
--- a/sound/soc/codecs/alc5623.h
+++ b/sound/soc/codecs/alc5623.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5623.h -- alc562[123] ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
*
* Author: flove <flove@realtek.com>
* Arnaud Patard <arnaud.patard@rtp-net.org>
- *
- * 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 _ALC5623_H
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index db3283abbe18..72f4622204ff 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* alc5632.c -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.c by Arnaud Patard
-*
-* 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.
*/
#include <linux/module.h>
@@ -35,7 +32,7 @@
/*
* ALC5632 register cache
*/
-static struct reg_default alc5632_reg_defaults[] = {
+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 */
@@ -116,20 +113,20 @@ static inline int alc5632_reset(struct regmap *map)
static int amp_mixer_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_write(codec, ALC5632_HID_CTRL_INDEX, 0x46);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_INDEX, 0x46);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0xFFFF);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0xFFFF);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_write(codec, ALC5632_HID_CTRL_DATA, 0);
+ snd_soc_component_write(component, ALC5632_HID_CTRL_DATA, 0);
break;
}
@@ -146,11 +143,10 @@ static const DECLARE_TLV_DB_SCALE(vol_tlv, -3450, 150, 0);
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 unsigned int boost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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 */
@@ -682,7 +678,7 @@ 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int gbl_clk = 0, pll_div = 0;
u16 reg;
@@ -690,15 +686,15 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* Disable PLL power */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_read(codec, ALC5632_DAI_CONTROL);
+ reg = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
if (reg & ALC5632_DAI_SDP_SLAVE_MODE)
return 0;
@@ -746,19 +742,19 @@ static int alc5632_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return -EINVAL;
/* choose MCLK/BCLK/VBCLK */
- snd_soc_write(codec, ALC5632_GPCR2, gbl_clk);
+ snd_soc_component_write(component, ALC5632_GPCR2, gbl_clk);
/* choose PLL1 clock rate */
- snd_soc_write(codec, ALC5632_PLL1_CTRL, pll_div);
+ snd_soc_component_write(component, ALC5632_PLL1_CTRL, pll_div);
/* enable PLL1 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_ADD2_PLL1,
ALC5632_PWR_ADD2_PLL1);
/* enable PLL2 */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_update_bits(codec, ALC5632_GPCR1,
+ snd_soc_component_update_bits(component, ALC5632_GPCR1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1,
ALC5632_GPCR1_CLK_SYS_SRC_SEL_PLL1);
@@ -776,9 +772,9 @@ static const struct _coeff_div coeff_div[] = {
{512*1, 0x3075},
};
-static int get_coeff(struct snd_soc_codec *codec, int rate)
+static int get_coeff(struct snd_soc_component *component, int rate)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
@@ -794,8 +790,8 @@ static int get_coeff(struct snd_soc_codec *codec, int rate)
static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (freq) {
case 4096000:
@@ -816,15 +812,15 @@ static int alc5632_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ 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:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = ALC5632_DAI_SDP_SLAVE_MODE;
break;
default:
@@ -865,17 +861,17 @@ static int alc5632_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- return snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int coeff, rate;
u16 iface;
- iface = snd_soc_read(codec, ALC5632_DAI_CONTROL);
+ iface = snd_soc_component_read(component, ALC5632_DAI_CONTROL);
iface &= ~ALC5632_DAI_I2S_DL_MASK;
/* bit size */
@@ -894,29 +890,29 @@ static int alc5632_pcm_hw_params(struct snd_pcm_substream *substream,
}
/* set iface & srate */
- snd_soc_write(codec, ALC5632_DAI_CONTROL, iface);
+ snd_soc_component_write(component, ALC5632_DAI_CONTROL, iface);
rate = params_rate(params);
- coeff = get_coeff(codec, rate);
+ coeff = get_coeff(component, rate);
if (coeff < 0)
return -EINVAL;
coeff = coeff_div[coeff].regvalue;
- snd_soc_write(codec, ALC5632_DAC_CLK_CTRL1, coeff);
+ snd_soc_component_write(component, ALC5632_DAC_CLK_CTRL1, coeff);
return 0;
}
-static int alc5632_mute(struct snd_soc_dai *dai, int mute)
+static int alc5632_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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_read(codec, ALC5632_MISC_CTRL) & ~hp_mute;
+ u16 mute_reg = snd_soc_component_read(component, ALC5632_MISC_CTRL) & ~hp_mute;
if (mute)
mute_reg |= hp_mute;
- return snd_soc_write(codec, ALC5632_MISC_CTRL, mute_reg);
+ return snd_soc_component_write(component, ALC5632_MISC_CTRL, mute_reg);
}
#define ALC5632_ADD2_POWER_EN (ALC5632_PWR_ADD2_VREF)
@@ -930,77 +926,76 @@ static int alc5632_mute(struct snd_soc_dai *dai, int mute)
| ALC5632_PWR_ADD1_HP_OUT_ENH_AMP \
| ALC5632_PWR_ADD1_MAIN_BIAS)
-static void enable_power_depop(struct snd_soc_codec *codec)
+static void enable_power_depop(struct snd_soc_component *component)
{
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_ADD1_SOFTGEN_EN,
ALC5632_PWR_ADD1_SOFTGEN_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_ADD3_POWER_EN,
ALC5632_ADD3_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ 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_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ snd_soc_component_update_bits(component, ALC5632_PWR_DOWN_CTRL_STATUS,
ALC5632_PWR_DOWN_CTRL_STATUS_MASK,
0);
msleep(500);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_ADD2_POWER_EN,
ALC5632_ADD2_POWER_EN);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_ADD1_POWER_EN,
ALC5632_ADD1_POWER_EN);
/* disable HP Depop2 */
- snd_soc_update_bits(codec, ALC5632_MISC_CTRL,
+ 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_codec *codec,
+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(codec);
+ enable_power_depop(component);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK,
ALC5632_PWR_ADD1_MAIN_BIAS);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ 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_update_bits(codec, ALC5632_PWR_DOWN_CTRL_STATUS,
+ 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_update_bits(codec, ALC5632_PWR_MANAG_ADD2,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD2,
ALC5632_PWR_MANAG_ADD2_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD3,
ALC5632_PWR_MANAG_ADD3_MASK, 0);
- snd_soc_update_bits(codec, ALC5632_PWR_MANAG_ADD1,
+ snd_soc_component_update_bits(component, ALC5632_PWR_MANAG_ADD1,
ALC5632_PWR_MANAG_ADD1_MASK, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1010,10 +1005,11 @@ static int alc5632_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops alc5632_dai_ops = {
.hw_params = alc5632_pcm_hw_params,
- .digital_mute = alc5632_mute,
+ .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 = {
@@ -1036,13 +1032,13 @@ static struct snd_soc_dai_driver alc5632_dai = {
.formats = ALC5632_FORMATS,},
.ops = &alc5632_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
#ifdef CONFIG_PM
-static int alc5632_resume(struct snd_soc_codec *codec)
+static int alc5632_resume(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
regcache_sync(alc5632->regmap);
@@ -1052,13 +1048,13 @@ static int alc5632_resume(struct snd_soc_codec *codec)
#define alc5632_resume NULL
#endif
-static int alc5632_probe(struct snd_soc_codec *codec)
+static int alc5632_probe(struct snd_soc_component *component)
{
- struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
+ struct alc5632_priv *alc5632 = snd_soc_component_get_drvdata(component);
switch (alc5632->id) {
case 0x5c:
- snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
+ snd_soc_add_component_controls(component, alc5632_vol_snd_controls,
ARRAY_SIZE(alc5632_vol_snd_controls));
break;
default:
@@ -1068,18 +1064,20 @@ static int alc5632_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_device_alc5632 = {
- .probe = alc5632_probe,
- .resume = alc5632_resume,
- .set_bias_level = alc5632_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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 = {
@@ -1093,18 +1091,24 @@ static const struct regmap_config alc5632_regmap = {
.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,
- const struct i2c_device_id *id)
+static int alc5632_i2c_probe(struct i2c_client *client)
{
struct alc5632_priv *alc5632;
int ret, ret1, ret2;
unsigned int vid1, vid2;
+ unsigned int matched_id;
alc5632 = devm_kzalloc(&client->dev,
sizeof(struct alc5632_priv), GFP_KERNEL);
@@ -1130,7 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client,
vid2 >>= 8;
- if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
+ matched_id = (uintptr_t)i2c_get_match_data(client);
+
+ if ((vid1 != 0x10EC) || (vid2 != matched_id)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
return -EINVAL;
@@ -1151,44 +1157,32 @@ static int alc5632_i2c_probe(struct i2c_client *client,
return -EINVAL;
}
- ret = snd_soc_register_codec(&client->dev,
- &soc_codec_device_alc5632, &alc5632_dai, 1);
+ 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 codec: %d\n", ret);
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int alc5632_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id alc5632_i2c_table[] = {
- {"alc5632", 0x5c},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
-
+#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",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(alc5632_of_match),
},
.probe = alc5632_i2c_probe,
- .remove = alc5632_i2c_remove,
.id_table = alc5632_i2c_table,
};
diff --git a/sound/soc/codecs/alc5632.h b/sound/soc/codecs/alc5632.h
index 1b5bda594ea3..a2bb5f9c7109 100644
--- a/sound/soc/codecs/alc5632.h
+++ b/sound/soc/codecs/alc5632.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* alc5632.h -- ALC5632 ALSA SoC Audio Codec
*
@@ -9,10 +10,6 @@
* Marc Dietrich <marvin24@gmx.de>
*
* Based on alc5623.h by Arnaud Patard
-*
-* 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 _ALC5632_H
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
new file mode 100644
index 000000000000..6b55610ad535
--- /dev/null
+++ b/sound/soc/codecs/arizona-jack.c
@@ -0,0 +1,1670 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * extcon-arizona.c - Extcon driver Wolfson Arizona devices
+ *
+ * Copyright (C) 2012-2014 Wolfson Microelectronics plc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+#include <dt-bindings/mfd/arizona.h>
+
+#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_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;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#else
+ int id_gpio = 0;
+#endif
+
+ 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;
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ /* 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;
+ }
+#endif
+
+ /* 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;
+#ifdef CONFIG_GPIOLIB_LEGACY
+ int id_gpio = arizona->pdata.hpdet_id_gpio;
+#endif
+ 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);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ if (id_gpio)
+ gpio_set_value_cansleep(id_gpio, 0);
+#endif
+
+ /* 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_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);
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ 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
+#endif
+ {
+ 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;
+ }
+ }
+
+#ifdef CONFIG_GPIOLIB_LEGACY
+ 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;
+ }
+ }
+#endif
+
+ 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
index 29202610dd0d..f2f47f1c1ac8 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1,25 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* arizona.c - Wolfson Arizona class device shared support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * 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.
*/
#include <linux/delay.h>
#include <linux/gcd.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <linux/mfd/arizona/core.h>
-#include <linux/mfd/arizona/gpio.h>
#include <linux/mfd/arizona/registers.h>
#include "arizona.h"
@@ -84,34 +81,14 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- bool manual_ena = false;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(component->dev->parent);
int val;
- switch (arizona->type) {
- case WM5102:
- switch (arizona->rev) {
- case 0:
- break;
- default:
- manual_ena = true;
- break;
- }
- default:
- break;
- }
-
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (!priv->spk_ena && manual_ena) {
- regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
- priv->spk_ena_pending = true;
- }
- break;
case SND_SOC_DAPM_POST_PMU:
- val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
+ 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");
@@ -121,36 +98,17 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 1 << w->shift);
-
- if (priv->spk_ena_pending) {
- msleep(75);
- regmap_write_async(arizona->regmap, 0x4f5, 0xda);
- priv->spk_ena_pending = false;
- priv->spk_ena++;
- }
break;
case SND_SOC_DAPM_PRE_PMD:
- if (manual_ena) {
- priv->spk_ena--;
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x25a);
- }
-
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 0);
break;
- case SND_SOC_DAPM_POST_PMD:
- if (manual_ena) {
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x0da);
- }
+ default:
break;
}
- return 0;
+ return arizona_out_ev(w, kcontrol, event);
}
static irqreturn_t arizona_thermal_warn(int irq, void *data)
@@ -200,34 +158,46 @@ static irqreturn_t arizona_thermal_shutdown(int irq, void *data)
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_PMD | SND_SOC_DAPM_POST_PMU);
+ 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_PMD | SND_SOC_DAPM_POST_PMU);
+ 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_codec *codec)
+int arizona_init_spk(struct snd_soc_component *component)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(&codec->dapm, &arizona_spkl, 1);
+ 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(&codec->dapm,
- &arizona_spkr, 1);
+ 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);
@@ -246,7 +216,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_init_spk);
+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" },
@@ -257,15 +236,16 @@ static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT6R", NULL, "OUT6L" },
};
-int arizona_init_mono(struct snd_soc_codec *codec)
+int arizona_init_mono(struct snd_soc_component *component)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
&arizona_mono_routes[i], 1);
}
@@ -273,31 +253,33 @@ int arizona_init_mono(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_mono);
-int arizona_init_gpio(struct snd_soc_codec *codec)
+int arizona_init_gpio(struct snd_soc_component *component)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
int i;
switch (arizona->type) {
case WM5110:
- snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+ case WM8280:
+ snd_soc_component_disable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
}
- snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+ 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_dapm_enable_pin(&codec->dapm,
- "DRC1 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC1 Signal Activity");
break;
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(&codec->dapm,
- "DRC2 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
@@ -308,12 +290,107 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
-const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
+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",
@@ -415,12 +492,13 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
};
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
-int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
+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 */
@@ -525,17 +603,42 @@ 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 *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
+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 int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE] = {
+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,
@@ -575,7 +678,7 @@ const struct soc_enum arizona_asrc_rate1 =
arizona_rate_text, arizona_rate_val);
EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
-static const char *arizona_vol_ramp_text[] = {
+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",
};
@@ -604,7 +707,7 @@ SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
-static const char *arizona_lhpf_mode_text[] = {
+static const char * const arizona_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
@@ -632,7 +735,7 @@ SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
-static const char *arizona_ng_hold_text[] = {
+static const char * const arizona_ng_hold_text[] = {
"30ms", "120ms", "250ms", "500ms",
};
@@ -672,9 +775,111 @@ const struct soc_enum arizona_in_dmic_osr[] = {
};
EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
-static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
+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_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int val;
int i;
@@ -684,16 +889,25 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
val = 0;
for (i = 0; i < priv->num_inputs; i++)
- snd_soc_update_bits(codec,
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -706,25 +920,29 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
priv->in_pending++;
break;
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, reg, ARIZONA_IN1L_MUTE, 0);
+ 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(codec, 1);
+ arizona_in_set_vu(component, 1);
}
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, reg,
+ 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_read(codec, ARIZONA_INPUT_ENABLES);
+ reg = snd_soc_component_read(component, ARIZONA_INPUT_ENABLES);
if (reg == 0)
- arizona_in_set_vu(codec, 0);
+ arizona_in_set_vu(component, 0);
+ break;
+ default:
+ break;
}
return 0;
@@ -735,8 +953,9 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -748,7 +967,19 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_up_pending++;
- priv->out_up_delay += 17;
+ priv->out_up_delay += 17000;
+ 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 += 10000;
+ break;
+ }
break;
default:
break;
@@ -762,9 +993,13 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
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) {
- msleep(priv->out_up_delay);
+ if (!priv->out_up_pending && priv->out_up_delay) {
+ dev_dbg(component->dev, "Power up delay: %d\n",
+ priv->out_up_delay);
+ fsleep(priv->out_up_delay);
priv->out_up_delay = 0;
}
break;
@@ -782,7 +1017,23 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
case ARIZONA_OUT3L_ENA_SHIFT:
case ARIZONA_OUT3R_ENA_SHIFT:
priv->out_down_pending++;
- priv->out_down_delay++;
+ priv->out_down_delay += 1000;
+ 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 += 5000;
+ break;
+ default:
+ priv->out_down_delay += 1000;
+ break;
+ }
break;
default:
break;
@@ -796,9 +1047,13 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
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) {
- msleep(priv->out_down_delay);
+ if (!priv->out_down_pending && priv->out_down_delay) {
+ dev_dbg(component->dev, "Power down delay: %d\n",
+ priv->out_down_delay);
+ fsleep(priv->out_down_delay);
priv->out_down_delay = 0;
}
break;
@@ -806,18 +1061,19 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
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)
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -840,8 +1096,8 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
priv->arizona->hp_ena &= ~mask;
priv->arizona->hp_ena |= val;
- /* Force off if HPDET magic is active */
- if (priv->arizona->hpdet_magic)
+ /* Force off if HPDET clamp is active */
+ if (priv->arizona->hpdet_clamp)
val = 0;
regmap_update_bits_async(arizona->regmap, ARIZONA_OUTPUT_ENABLES_1,
@@ -851,30 +1107,176 @@ int arizona_hp_ev(struct snd_soc_dapm_widget *w,
}
EXPORT_SYMBOL_GPL(arizona_hp_ev);
-static unsigned int arizona_sysclk_48k_rates[] = {
+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,
- 73728000,
- 98304000,
- 147456000,
};
-static unsigned int arizona_sysclk_44k1_rates[] = {
+static unsigned int arizona_opclk_ref_44k1_rates[] = {
5644800,
11289600,
22579200,
45158400,
- 67737600,
- 90316800,
- 135475200,
};
-static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
- unsigned int freq)
+static int arizona_set_opclk(struct snd_soc_component *component,
+ unsigned int clk, unsigned int freq)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
unsigned int reg;
unsigned int *rates;
int ref, div, refclk;
@@ -893,18 +1295,18 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
if (refclk % 8000)
- rates = arizona_sysclk_44k1_rates;
+ rates = arizona_opclk_ref_44k1_rates;
else
- rates = arizona_sysclk_48k_rates;
+ rates = arizona_opclk_ref_48k_rates;
- for (ref = 0; ref < ARRAY_SIZE(arizona_sysclk_48k_rates) &&
- rates[ref] <= refclk; ref++) {
+ 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(codec->dev, "Configured %dHz OPCLK\n",
+ dev_dbg(component->dev, "Configured %dHz OPCLK\n",
freq);
- snd_soc_update_bits(codec, reg,
+ snd_soc_component_update_bits(component, reg,
ARIZONA_OPCLK_DIV_MASK |
ARIZONA_OPCLK_SEL_MASK,
(div <<
@@ -916,20 +1318,60 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
}
}
- dev_err(codec->dev, "Unable to generate %dHz OPCLK\n", freq);
+ dev_err(component->dev, "Unable to generate %dHz OPCLK\n", freq);
return -EINVAL;
}
-int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ 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;
- unsigned int *clk;
+ int *clk;
switch (clk_id) {
case ARIZONA_CLK_SYSCLK:
@@ -945,7 +1387,7 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
break;
case ARIZONA_CLK_OPCLK:
case ARIZONA_CLK_ASYNC_OPCLK:
- return arizona_set_opclk(codec, clk_id, freq);
+ return arizona_set_opclk(component, clk_id, freq);
default:
return -EINVAL;
}
@@ -999,8 +1441,8 @@ EXPORT_SYMBOL_GPL(arizona_set_sysclk);
static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1015,7 +1457,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_DSP_B:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
return -EINVAL;
}
@@ -1026,7 +1468,7 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
break;
case SND_SOC_DAIFMT_LEFT_J:
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
- != SND_SOC_DAIFMT_CBM_CFM) {
+ != SND_SOC_DAIFMT_CBP_CFP) {
arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
return -EINVAL;
}
@@ -1039,15 +1481,15 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
bclk |= ARIZONA_AIF1_BCLK_MSTR;
lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
break;
@@ -1113,29 +1555,6 @@ static const int arizona_48k_bclk_rates[] = {
24576000,
};
-static const unsigned int arizona_48k_rates[] = {
- 12000,
- 24000,
- 48000,
- 96000,
- 192000,
- 384000,
- 768000,
- 4000,
- 8000,
- 16000,
- 32000,
- 64000,
- 128000,
- 256000,
- 512000,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
- .count = ARRAY_SIZE(arizona_48k_rates),
- .list = arizona_48k_rates,
-};
-
static const int arizona_44k1_bclk_rates[] = {
-1,
44100,
@@ -1158,22 +1577,7 @@ static const int arizona_44k1_bclk_rates[] = {
22579200,
};
-static const unsigned int arizona_44k1_rates[] = {
- 11025,
- 22050,
- 44100,
- 88200,
- 176400,
- 352800,
- 705600,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
- .count = ARRAY_SIZE(arizona_44k1_rates),
- .list = arizona_44k1_rates,
-};
-
-static int arizona_sr_vals[] = {
+static const unsigned int arizona_sr_vals[] = {
0,
12000,
24000,
@@ -1200,15 +1604,26 @@ static int arizona_sr_vals[] = {
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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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];
- const struct snd_pcm_hw_constraint_list *constraint;
unsigned int base_rate;
+ if (!substream->runtime)
+ return 0;
+
switch (dai_priv->clk) {
case ARIZONA_CLK_SYSCLK:
base_rate = priv->sysclk;
@@ -1221,24 +1636,23 @@ static int arizona_startup(struct snd_pcm_substream *substream,
}
if (base_rate == 0)
- return 0;
-
- if (base_rate % 8000)
- constraint = &arizona_44k1_constraint;
+ dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+ else if (base_rate % 8000)
+ dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
else
- constraint = &arizona_48k_constraint;
+ dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
- constraint);
+ &dai_priv->constraint);
}
-static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
+static void arizona_wm5102_set_dac_comp(struct snd_soc_component *component,
unsigned int rate)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_priv *priv = snd_soc_component_get_drvdata(component);
struct arizona *arizona = priv->arizona;
- struct reg_default dac_comp[] = {
+ struct reg_sequence dac_comp[] = {
{ 0x80, 0x3 },
{ ARIZONA_DAC_COMP_1, 0 },
{ ARIZONA_DAC_COMP_2, 0 },
@@ -1262,11 +1676,11 @@ 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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
+ int i, sr_val, ret;
/*
* We will need to be more flexible than this in future,
@@ -1282,30 +1696,52 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
}
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(codec,
+ arizona_wm5102_set_dac_comp(component,
params_rate(params));
break;
default:
break;
}
- snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
- ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component, ARIZONA_SAMPLE_RATE_1,
+ ARIZONA_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK, 0);
+ snd_soc_component_update_bits(component,
+ base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK, 0);
break;
case ARIZONA_CLK_ASYNCCLK:
- snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
- ARIZONA_ASYNC_SAMPLE_RATE_1_MASK, sr_val);
+ snd_soc_component_update_bits(component,
+ ARIZONA_ASYNC_SAMPLE_RATE_1,
+ ARIZONA_ASYNC_SAMPLE_RATE_1_MASK,
+ sr_val);
if (base)
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK,
- 8 << ARIZONA_AIF1_RATE_SHIFT);
+ 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);
@@ -1315,20 +1751,20 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
return 0;
}
-static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+static bool arizona_aif_cfg_changed(struct snd_soc_component *component,
int base, int bclk, int lrclk, int frame)
{
int val;
- val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL);
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_BCLK_CTRL);
if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE);
- if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK))
return true;
- val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+ 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;
@@ -1340,8 +1776,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1354,12 +1790,12 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
bool reconfig;
unsigned int aif_tx_state, aif_rx_state;
- if (params_rate(params) % 8000)
+ if (params_rate(params) % 4000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
- wl = snd_pcm_format_width(params_format(params));
+ wl = params_width(params);
if (tdm_slots) {
arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
@@ -1378,7 +1814,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
}
/* Force multiple of 2 channels for I2S mode */
- val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
+ 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");
@@ -1406,19 +1842,20 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
frame = wl << ARIZONA_AIF1TX_WL_SHIFT | tdm_width;
- reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+ reconfig = arizona_aif_cfg_changed(component, base, bclk, lrclk, frame);
if (reconfig) {
/* Save AIF TX/RX state */
- aif_tx_state = snd_soc_read(codec,
+ aif_tx_state = snd_soc_component_read(component,
base + ARIZONA_AIF_TX_ENABLES);
- aif_rx_state = snd_soc_read(codec,
+ 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);
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, 0x0);
regmap_update_bits(arizona->regmap,
- base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
}
ret = arizona_hw_params_rate(substream, params, dai);
@@ -1473,8 +1910,9 @@ static const char *arizona_dai_clk_str(int clk_id)
static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -1489,13 +1927,13 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
if (clk_id == dai_priv->clk)
return 0;
- if (dai->active) {
- dev_err(codec->dev, "Can't change clock on active DAI %d\n",
+ 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(codec->dev, "Setting AIF%d to %s\n", dai->id + 1,
+ dev_dbg(component->dev, "Setting AIF%d to %s\n", dai->id + 1,
arizona_dai_clk_str(clk_id));
memset(&routes, 0, sizeof(routes));
@@ -1504,20 +1942,20 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
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(&codec->dapm, routes, ARRAY_SIZE(routes));
+ 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(&codec->dapm, routes, ARRAY_SIZE(routes));
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
dai_priv->clk = clk_id;
- return snd_soc_dapm_sync(&codec->dapm);
+ return snd_soc_dapm_sync(dapm);
}
static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int base = dai->driver->base;
unsigned int reg;
@@ -1526,16 +1964,17 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
else
reg = 0;
- return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_TRI, reg);
+ 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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1556,8 +1995,8 @@ static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1605,22 +2044,12 @@ 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 irqreturn_t arizona_fll_clock_ok(int irq, void *data)
-{
- struct arizona_fll *fll = data;
-
- arizona_fll_dbg(fll, "clock OK\n");
-
- complete(&fll->ok);
-
- return IRQ_HANDLED;
-}
-
static struct {
unsigned int min;
unsigned int max;
@@ -1634,6 +2063,25 @@ static struct {
{ 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;
@@ -1646,8 +2094,8 @@ static struct {
struct arizona_fll_cfg {
int n;
- int theta;
- int lambda;
+ unsigned int theta;
+ unsigned int lambda;
int refdiv;
int outdiv;
int fratio;
@@ -1728,12 +2176,18 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
}
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:
- return init_ratio;
+ if (sync)
+ return init_ratio;
+ break;
}
cfg->fratio = init_ratio - 1;
@@ -1741,24 +2195,46 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
/* Adjust FRATIO/refdiv to avoid integer mode if possible */
refdiv = cfg->refdiv;
- while (div <= ARIZONA_FLL_MAX_REFDIV) {
- for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
- ratio++) {
- if ((ARIZONA_FLL_VCO_CORNER / 2) /
- (fll->vco_mult * ratio) < Fref)
- break;
+ 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 > 0; 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;
}
}
@@ -1767,6 +2243,9 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
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");
@@ -1837,14 +2316,14 @@ static int arizona_calc_fll(struct arizona_fll *fll,
return -EINVAL;
}
- arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+ arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
cfg->n, cfg->theta, cfg->lambda);
- arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
- cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
- arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+ 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,
@@ -1882,13 +2361,13 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
ARIZONA_FLL1_CTRL_UPD | cfg->n);
}
-static int arizona_is_enabled_fll(struct arizona_fll *fll)
+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, fll->base + 1, &reg);
+ ret = regmap_read(arizona->regmap, base + 1, &reg);
if (ret != 0) {
arizona_fll_err(fll, "Failed to read current state: %d\n",
ret);
@@ -1898,24 +2377,68 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll)
return reg & ARIZONA_FLL1_ENA;
}
-static int arizona_enable_fll(struct arizona_fll *fll)
+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);
+ 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_async(fll->arizona->regmap, fll->base + 0x7,
+ 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);
- regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_FREERUN,
- ARIZONA_FLL1_FREERUN);
+
+ 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);
}
/*
@@ -1926,6 +2449,10 @@ static int arizona_enable_fll(struct arizona_fll *fll)
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) {
@@ -1948,6 +2475,9 @@ static int arizona_enable_fll(struct arizona_fll *fll)
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.
@@ -1961,26 +2491,40 @@ static int arizona_enable_fll(struct arizona_fll *fll)
ARIZONA_FLL1_SYNC_BW);
if (!already_enabled)
- pm_runtime_get(arizona->dev);
-
- /* Clear any pending completions */
- try_wait_for_completion(&fll->ok);
+ pm_runtime_get_sync(arizona->dev);
- regmap_update_bits_async(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
- if (use_sync)
+ 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);
- ret = wait_for_completion_timeout(&fll->ok,
- msecs_to_jiffies(250));
- if (ret == 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;
}
@@ -1988,19 +2532,24 @@ static int arizona_enable_fll(struct arizona_fll *fll)
static void arizona_disable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
- bool change;
+ 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, &change);
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA, 0);
+ 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 (change)
+ 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,
@@ -2020,9 +2569,8 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
fll->ref_src = source;
fll->ref_freq = Fref;
- if (fll->fout && Fref > 0) {
+ if (fll->fout && Fref > 0)
ret = arizona_enable_fll(fll);
- }
return ret;
}
@@ -2065,11 +2613,8 @@ 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)
{
- int ret;
unsigned int val;
- init_completion(&fll->ok);
-
fll->id = id;
fll->base = base;
fll->arizona = arizona;
@@ -2091,13 +2636,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
"FLL%d clock OK", id);
- ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
- arizona_fll_clock_ok, fll);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n",
- id, ret);
- }
-
regmap_update_bits(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, 0);
@@ -2108,7 +2646,7 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
/**
* arizona_set_output_mode - Set the mode of the specified output
*
- * @codec: Device to configure
+ * @component: Device to configure
* @output: Output number
* @diff: True to set the output to differential mode
*
@@ -2121,7 +2659,8 @@ EXPORT_SYMBOL_GPL(arizona_init_fll);
* Most systems have a single static configuration and should use
* platform data instead.
*/
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+int arizona_set_output_mode(struct snd_soc_component *component, int output,
+ bool diff)
{
unsigned int reg, val;
@@ -2135,10 +2674,186 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
else
val = 0;
- return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+ 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;
+ u32 val;
+ u32 pdm_val[ARIZONA_MAX_PDM_SPK];
+ int ret;
+ int count = 0;
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,inmode", val) {
+ if (count == ARRAY_SIZE(pdata->inmode))
+ break;
+
+ pdata->inmode[count] = val;
+ count++;
+ }
+
+ count = 0;
+ of_property_for_each_u32(np, "wlf,dmic-ref", 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", 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", 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", 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 <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 11ff899b0272..ecd8890eefc1 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -1,19 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* arizona.h - Wolfson Arizona class device shared support
*
* Copyright 2012 Wolfson Microelectronics plc
*
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * 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 _ASOC_ARIZONA_H
#define _ASOC_ARIZONA_H
#include <linux/completion.h>
+#include <linux/notifier.h>
+#include <linux/mfd/arizona/core.h>
#include <sound/soc.h>
@@ -57,14 +56,21 @@
#define ARIZONA_CLK_98MHZ 5
#define ARIZONA_CLK_147MHZ 6
-#define ARIZONA_MAX_DAI 6
+#define ARIZONA_MAX_DAI 10
#define ARIZONA_MAX_ADSP 4
-struct arizona;
+#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 {
@@ -82,15 +88,60 @@ struct arizona_priv {
unsigned int out_down_pending;
unsigned int out_down_delay;
- unsigned int spk_ena:2;
- unsigned int spk_ena_pending:1;
+ 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 103
+#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
-extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
-extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+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, \
@@ -107,8 +158,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
arizona_mixer_tlv)
#define ARIZONA_MUX_ENUM_DECL(name, reg) \
- SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff, \
- arizona_mixer_texts, arizona_mixer_values)
+ 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 = \
@@ -172,24 +223,47 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
#define ARIZONA_DSP_ROUTES(name) \
{ name, NULL, name " Preloader"}, \
- { name " Preloader", NULL, name " Aux 1" }, \
- { name " Preloader", NULL, name " Aux 2" }, \
- { name " Preloader", NULL, name " Aux 3" }, \
- { name " Preloader", NULL, name " Aux 4" }, \
- { name " Preloader", NULL, name " Aux 5" }, \
- { name " Preloader", NULL, name " Aux 6" }, \
+ { 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 " Preloader", name "L"), \
- ARIZONA_MIXER_ROUTES(name " Preloader", name "R")
+ 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
-extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
-extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+#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[];
@@ -210,18 +284,32 @@ 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 int arizona_in_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
+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);
-extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
- int source, unsigned int freq, int dir);
+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;
@@ -233,7 +321,6 @@ struct arizona_fll {
int id;
unsigned int base;
unsigned int vco_mult;
- struct completion ok;
unsigned int fout;
int sync_src;
@@ -245,20 +332,67 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
-extern int arizona_init_fll(struct arizona *arizona, int id, int base,
- int lock_irq, int ok_irq, struct arizona_fll *fll);
-extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
- unsigned int Fref, unsigned int Fout);
-extern int arizona_set_fll(struct arizona_fll *fll, int source,
+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);
-extern int arizona_init_spk(struct snd_soc_codec *codec);
-extern int arizona_init_gpio(struct snd_soc_codec *codec);
-extern int arizona_init_mono(struct snd_soc_codec *codec);
+int arizona_init_common(struct arizona *arizona);
+int arizona_init_vol_limit(struct arizona *arizona);
-extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+int arizona_init_spk_irqs(struct arizona *arizona);
+int arizona_free_spk_irqs(struct arizona *arizona);
-int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+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 000000000000..588e48044c13
--- /dev/null
+++ b/sound/soc/codecs/audio-iio-aux.c
@@ -0,0 +1,314 @@
+// 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 <herve.codina@bootlin.com>
+
+#include <linux/cleanup.h>
+#include <linux/iio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string_helpers.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+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;
+ unsigned int num_chans;
+ struct audio_iio_aux_chan chans[] __counted_by(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);
+ int ret;
+
+ /* Allocated names are not needed afterwards (duplicated in ASoC internals) */
+ char *input_name __free(kfree) = kasprintf(GFP_KERNEL, "%s IN", chan->name);
+ if (!input_name)
+ return -ENOMEM;
+
+ char *output_name __free(kfree) = kasprintf(GFP_KERNEL, "%s OUT", chan->name);
+ if (!output_name)
+ return -ENOMEM;
+
+ char *pga_name __free(kfree) = kasprintf(GFP_KERNEL, "%s PGA", chan->name);
+ if (!pga_name)
+ return -ENOMEM;
+
+ 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)
+ return ret;
+
+ 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;
+
+ return snd_soc_dapm_add_routes(dapm, routes, 2);
+}
+
+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;
+ int count;
+ int ret;
+ int i;
+
+ 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 = devm_kzalloc(dev, struct_size(iio_aux, chans, count), GFP_KERNEL);
+ if (!iio_aux)
+ return -ENOMEM;
+
+ iio_aux->dev = dev;
+
+ iio_aux->num_chans = count;
+
+ const char **names __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*names),
+ GFP_KERNEL);
+ if (!names)
+ return -ENOMEM;
+
+ u32 *invert_ranges __free(kfree) = kcalloc(iio_aux->num_chans,
+ sizeof(*invert_ranges),
+ GFP_KERNEL);
+ if (!invert_ranges)
+ return -ENOMEM;
+
+ ret = device_property_read_string_array(dev, "io-channel-names",
+ names, iio_aux->num_chans);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to read io-channel-names\n");
+
+ /*
+ * 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)
+ return dev_err_probe(dev, ret, "failed to read snd-control-invert-range\n");
+ }
+
+ 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))
+ return dev_err_probe(dev, PTR_ERR(iio_aux_chan->iio_chan),
+ "get IIO channel '%s' failed\n",
+ iio_aux_chan->name);
+ }
+
+ platform_set_drvdata(pdev, iio_aux);
+
+ return devm_snd_soc_register_component(dev, &audio_iio_aux_component_driver,
+ NULL, 0);
+}
+
+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 <herve.codina@bootlin.com>");
+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 000000000000..0fe8af160319
--- /dev/null
+++ b/sound/soc/codecs/aw8738.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+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/aw87390.c b/sound/soc/codecs/aw87390.c
new file mode 100644
index 000000000000..ef6f64856988
--- /dev/null
+++ b/sound/soc/codecs/aw87390.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.c -- AW87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw87390.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw87390_remap_config = {
+ .val_bits = 8,
+ .reg_bits = 8,
+ .max_register = AW87390_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw87390_dev_reg_update(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int i, ret;
+
+ if (!data) {
+ dev_err(aw_dev->dev, "data is NULL\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len-1; i += 2) {
+ if (data[i] == AW87390_DELAY_REG_ADDR) {
+ usleep_range(data[i + 1] * AW87390_REG_DELAY_TIME,
+ data[i + 1] * AW87390_REG_DELAY_TIME + 10);
+ continue;
+ }
+ ret = regmap_write(aw_dev->regmap, data[i], data[i + 1]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int aw87390_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw87390_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 aw87390_dev_fw_update(struct aw_device *aw_dev)
+{
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw87390_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw87390_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw87390_dev_get_prof_data failed\n");
+ return ret;
+ }
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw87390_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\n");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static int aw87390_power_off(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off\n");
+ return 0;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ return 0;
+}
+
+static int aw87390_power_on(struct aw_device *aw_dev)
+{
+ int ret;
+
+ if (aw_dev->status == AW87390_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on\n");
+ return 0;
+ }
+
+ if (!aw_dev->fw_status) {
+ dev_err(aw_dev->dev, "fw not load\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW87390_SYSCTRL_REG, AW87390_POWER_DOWN_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw87390_dev_fw_update(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s load profile failed\n", __func__);
+ return ret;
+ }
+ aw_dev->status = AW87390_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw87390_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw87390_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw87390->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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw87390_dev_get_prof_name(aw87390->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw87390_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw87390->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw87390_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw87390->lock);
+ ret = aw87390_dev_set_profile_index(aw87390->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change\n");
+ mutex_unlock(&aw87390->lock);
+ return 0;
+ }
+
+ if (aw87390->aw_pa->status == AW87390_DEV_PW_ON) {
+ aw87390_power_off(aw87390->aw_pa);
+ aw87390_power_on(aw87390->aw_pa);
+ }
+
+ mutex_unlock(&aw87390->lock);
+
+ return 1;
+}
+
+static const struct snd_kcontrol_new aw87390_controls[] = {
+ AW87390_PROFILE_EXT("AW87390 Profile Set", aw87390_profile_info,
+ aw87390_profile_get, aw87390_profile_set),
+};
+
+static int aw87390_request_firmware_file(struct aw87390 *aw87390)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw87390->aw_pa->fw_status = AW87390_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW87390_ACF_FILE, aw87390->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "load [%s] failed!\n", AW87390_ACF_FILE);
+
+ dev_dbg(aw87390->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW87390_ACF_FILE, cont ? cont->size : 0);
+
+ aw87390->aw_cfg = devm_kzalloc(aw87390->aw_pa->dev,
+ struct_size(aw87390->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw87390->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+
+ aw87390->aw_cfg->len = cont->size;
+ memcpy(aw87390->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret) {
+ dev_err(aw87390->aw_pa->dev, "load [%s] failed!\n", AW87390_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw87390->lock);
+
+ ret = aw88395_dev_cfg_load(aw87390->aw_pa, aw87390->aw_cfg);
+ if (ret)
+ dev_err(aw87390->aw_pa->dev, "aw_dev acf parse failed\n");
+
+ mutex_unlock(&aw87390->lock);
+
+ return ret;
+}
+
+static int aw87390_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 aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = aw87390_power_on(aw_dev);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = aw87390_power_off(aw_dev);
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: invalid event %d\n", __func__, event);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget aw87390_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL, 0, aw87390_drv_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route aw87390_dapm_routes[] = {
+ { "SPK PA", NULL, "IN" },
+ { "OUT", NULL, "SPK PA" },
+};
+
+static int aw87390_codec_probe(struct snd_soc_component *component)
+{
+ struct aw87390 *aw87390 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = aw87390_request_firmware_file(aw87390);
+ if (ret)
+ return dev_err_probe(aw87390->aw_pa->dev, ret,
+ "aw87390_request_firmware_file failed\n");
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw87390 = {
+ .probe = aw87390_codec_probe,
+ .dapm_widgets = aw87390_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw87390_dapm_widgets),
+ .dapm_routes = aw87390_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aw87390_dapm_routes),
+ .controls = aw87390_controls,
+ .num_controls = ARRAY_SIZE(aw87390_controls),
+};
+
+static void aw87390_parse_channel_dt(struct aw87390 *aw87390)
+{
+ struct aw_device *aw_dev = aw87390->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW87390_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw87390_init(struct aw87390 **aw87390, 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, AW87390_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (chip_id != AW87390_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ (*aw87390)->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW87390_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 = AW87390_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW87390_DEV_FW_FAILED;
+ aw_dev->prof_index = AW87390_INIT_PROFILE;
+ aw_dev->status = AW87390_DEV_PW_OFF;
+
+ aw87390_parse_channel_dt(*aw87390);
+
+ return 0;
+}
+
+static int aw87390_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw87390 *aw87390;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw87390 = devm_kzalloc(&i2c->dev, sizeof(*aw87390), GFP_KERNEL);
+ if (!aw87390)
+ return -ENOMEM;
+
+ mutex_init(&aw87390->lock);
+
+ i2c_set_clientdata(i2c, aw87390);
+
+ aw87390->regmap = devm_regmap_init_i2c(i2c, &aw87390_remap_config);
+ if (IS_ERR(aw87390->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw87390->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw87390_init(&aw87390, i2c, aw87390->regmap);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw87390->regmap, AW87390_ID_REG, AW87390_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw87390, NULL, 0);
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw87390: %d\n", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw87390_i2c_id[] = {
+ { AW87390_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
+
+static struct i2c_driver aw87390_i2c_driver = {
+ .driver = {
+ .name = AW87390_I2C_NAME,
+ },
+ .probe = aw87390_i2c_probe,
+ .id_table = aw87390_i2c_id,
+};
+module_i2c_driver(aw87390_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW87390 PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw87390.h b/sound/soc/codecs/aw87390.h
new file mode 100644
index 000000000000..d0d049e65991
--- /dev/null
+++ b/sound/soc/codecs/aw87390.h
@@ -0,0 +1,85 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw87390.h -- aw87390 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW87390_H__
+#define __AW87390_H__
+
+#define AW87390_ID_REG (0x00)
+#define AW87390_SYSCTRL_REG (0x01)
+#define AW87390_MDCTRL_REG (0x02)
+#define AW87390_CPOVP_REG (0x03)
+#define AW87390_CPP_REG (0x04)
+#define AW87390_PAG_REG (0x05)
+#define AW87390_AGC3P_REG (0x06)
+#define AW87390_AGC3PA_REG (0x07)
+#define AW87390_AGC2P_REG (0x08)
+#define AW87390_AGC2PA_REG (0x09)
+#define AW87390_AGC1PA_REG (0x0A)
+#define AW87390_SYSST_REG (0x59)
+#define AW87390_SYSINT_REG (0x60)
+#define AW87390_DFT_SYSCTRL_REG (0x61)
+#define AW87390_DFT_MDCTRL_REG (0x62)
+#define AW87390_DFT_CPADP_REG (0x63)
+#define AW87390_DFT_AGCPA_REG (0x64)
+#define AW87390_DFT_POFR_REG (0x65)
+#define AW87390_DFT_OC_REG (0x66)
+#define AW87390_DFT_ADP1_REG (0x67)
+#define AW87390_DFT_REF_REG (0x68)
+#define AW87390_DFT_LDO_REG (0x69)
+#define AW87390_ADP1_REG (0x70)
+#define AW87390_ADP2_REG (0x71)
+#define AW87390_NG1_REG (0x72)
+#define AW87390_NG2_REG (0x73)
+#define AW87390_NG3_REG (0x74)
+#define AW87390_CP_REG (0x75)
+#define AW87390_AB_REG (0x76)
+#define AW87390_TEST_REG (0x77)
+#define AW87390_ENCR_REG (0x78)
+#define AW87390_DELAY_REG_ADDR (0xFE)
+
+#define AW87390_SOFT_RESET_VALUE (0xAA)
+#define AW87390_POWER_DOWN_VALUE (0x00)
+#define AW87390_REG_MAX (0xFF)
+#define AW87390_DEV_DEFAULT_CH (0)
+#define AW87390_INIT_PROFILE (0)
+#define AW87390_REG_DELAY_TIME (1000)
+#define AW87390_I2C_NAME "aw87390"
+#define AW87390_ACF_FILE "aw87390_acf.bin"
+
+#define AW87390_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 aw87390_id {
+ AW87390_CHIP_ID = 0x76,
+};
+
+enum {
+ AW87390_DEV_FW_FAILED = 0,
+ AW87390_DEV_FW_OK,
+};
+
+enum {
+ AW87390_DEV_PW_OFF = 0,
+ AW87390_DEV_PW_ON,
+};
+
+struct aw87390 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c
new file mode 100644
index 000000000000..d61a7b8c5470
--- /dev/null
+++ b/sound/soc/codecs/aw88081.c
@@ -0,0 +1,1317 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.c -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88081.h"
+#include "aw88395/aw88395_device.h"
+
+enum aw8808x_type {
+ AW88081,
+ AW88083,
+};
+
+struct aw88081 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+ enum aw8808x_type devtype;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88081_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88081_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_config aw88083_regmap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88083_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static int aw88081_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88081_BIT_PLL_CHECK) != AW88081_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88081_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88081_CCO_MUX_MASK);
+ if (reg_val == AW88081_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, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88081_PLLCTRL1_REG,
+ ~AW88081_CCO_MUX_MASK, AW88081_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis check error");
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88081_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88081_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int aw88081_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ unsigned int value;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88081_PWMCTRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88081_NOISE_GATE_EN_MASK))
+ check_val = AW88081_NO_SWS_SYSST_CHECK;
+ else
+ check_val = AW88081_SWS_SYSST_CHECK;
+
+ for (i = 0; i < AW88081_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88081_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ value = reg_val & (~AW88081_BIT_SYSST_CHECK_MASK) & check_val;
+ if (value != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, check_val);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88081_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_I2SCTRL3_REG,
+ ~AW88081_I2STXEN_MASK, AW88081_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88081_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_PWDN_MASK, AW88081_PWDN_WORKING_VALUE);
+}
+
+static void aw88081_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_EN_PA_MASK, AW88081_EN_PA_WORKING_VALUE);
+}
+
+static void aw88083_i2c_wen(struct aw88081 *aw88081, bool flag)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw88081->devtype != AW88083)
+ return;
+
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_DISABLE_VALUE);
+}
+
+static void aw88083_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_AMPPD_MASK, AW88083_AMPPD_WORKING_VALUE);
+}
+
+static void aw88083_dev_pllpd(struct aw_device *aw_dev, bool pllpd)
+{
+ if (pllpd)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_WORKING_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88083_PLL_PD_MASK, AW88083_PLL_PD_POWER_DOWN_VALUE);
+}
+
+static void aw88081_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88081_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static void aw88081_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int volume;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88081_MUTE_VOL);
+
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL2_REG, ~AW88081_VOL_MASK, volume);
+}
+
+static void aw88081_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) {
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88081_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time, aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88081_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88081_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) {
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88081_MUTE_VOL; i += fade_step) {
+ aw88081_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88081_MUTE_VOL)
+ aw88081_dev_set_volume(aw_dev, AW88081_MUTE_VOL);
+}
+
+static void aw88081_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88081_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_HMUTE_MASK, AW88081_HMUTE_DISABLE_VALUE);
+ aw88081_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88081_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
+ ~AW88081_ULS_HMUTE_MASK,
+ AW88081_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static int aw88081_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88081_EN_PA_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88081_ULS_HMUTE_MASK);
+
+ *reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88081_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88081_I2SCTRL3_REG) {
+ /* close tx */
+ *reg_val &= AW88081_I2STXEN_MASK;
+ *reg_val |= AW88081_I2STXEN_DISABLE_VALUE;
+ }
+
+ return 0;
+}
+
+static int aw88083_dev_reg_value_check(struct aw_device *aw_dev,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ unsigned int read_vol;
+
+ if (reg_addr == AW88081_SYSCTRL_REG) {
+ *reg_val &= ~(~AW88083_AMPPD_MASK |
+ ~AW88081_PWDN_MASK |
+ ~AW88081_HMUTE_MASK |
+ ~AW88083_I2C_WEN_MASK);
+
+ *reg_val |= AW88083_AMPPD_POWER_DOWN_VALUE |
+ AW88081_PWDN_POWER_DOWN_VALUE |
+ AW88081_HMUTE_ENABLE_VALUE |
+ AW88083_I2C_WEN_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88081_SYSCTRL2_REG) {
+ read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ return 0;
+}
+
+static int aw88081_reg_value_check(struct aw88081 *aw88081,
+ unsigned char reg_addr, unsigned short *reg_val)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ case AW88083:
+ ret = aw88083_dev_reg_value_check(aw_dev, reg_addr, reg_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_reg_update(struct aw88081 *aw88081,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ 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];
+
+ ret = aw88081_reg_value_check(aw88081, reg_addr, &reg_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88081_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return 0;
+}
+
+static int aw88081_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88081_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 aw88081_dev_fw_update(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ ret = aw88081_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88081_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 = aw88081_dev_reg_update(aw88081, 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 0;
+}
+
+static int aw88081_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ ret = aw88081_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88081_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88081_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, true);
+
+ /* close uls mute */
+ aw88081_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ aw88081_dev_clear_int_status(aw_dev);
+ aw88081_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88081_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88083_dev_start(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88083_i2c_wen(aw88081, true);
+
+ /* power on */
+ aw88081_dev_pwd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
+
+ aw88083_dev_pllpd(aw_dev, true);
+ /* amppd on */
+ aw88083_dev_amppd(aw_dev, false);
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 50);
+
+ /* close mute */
+ aw88081_dev_mute(aw_dev, false);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+
+ return 0;
+}
+
+static int aw88081_device_start(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_start(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_start(aw88081);
+ break;
+ default:
+ ret = -EINVAL;
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88081_dev_clear_int_status(aw_dev);
+
+ aw88081_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88081_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88081_1000_US, AW88081_1000_US + 100);
+
+ /* enable amppd */
+ aw88081_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88083_dev_stop(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ if (aw_dev->status == AW88081_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_OFF;
+
+ aw88083_i2c_wen(aw88081, true);
+ /* set mute */
+ aw88081_dev_mute(aw_dev, true);
+
+ usleep_range(AW88081_2000_US, AW88081_2000_US + 100);
+
+ /* enable amppd */
+ aw88083_dev_amppd(aw_dev, true);
+
+ aw88083_dev_pllpd(aw_dev, false);
+
+ /* set power down */
+ aw88081_dev_pwd(aw_dev, true);
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_stop(struct aw88081 *aw88081)
+{
+ int ret;
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ ret = aw88081_dev_stop(aw88081);
+ break;
+ case AW88083:
+ ret = aw88083_dev_stop(aw88081);
+ break;
+ default:
+ dev_err(aw88081->aw_pa->dev, "unsupported device\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw88081_reg_update(struct aw88081 *aw88081, bool force)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret)
+ return ret;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+}
+
+static void aw88081_start_pa(struct aw88081 *aw88081)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88081_START_RETRIES; i++) {
+ ret = aw88081_reg_update(aw88081, aw88081->phase_sync);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88081_device_start(aw88081);
+ if (ret) {
+ dev_err(aw88081->aw_pa->dev, "aw88081 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_dbg(aw88081->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88081_startup_work(struct work_struct *work)
+{
+ struct aw88081 *aw88081 =
+ container_of(work, struct aw88081, start_work.work);
+
+ mutex_lock(&aw88081->lock);
+ aw88081_start_pa(aw88081);
+ mutex_unlock(&aw88081->lock);
+}
+
+static void aw88081_start(struct aw88081 *aw88081, bool sync_start)
+{
+ if (aw88081->aw_pa->fw_status != AW88081_DEV_FW_OK)
+ return;
+
+ if (aw88081->aw_pa->status == AW88081_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88081_SYNC_START)
+ aw88081_start_pa(aw88081);
+ else
+ queue_delayed_work(system_wq,
+ &aw88081->start_work,
+ AW88081_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88081_dai[] = {
+ {
+ .name = "aw88081-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88081_RATES,
+ .formats = AW88081_FORMATS,
+ },
+ },
+};
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->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 aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88081->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88081->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 aw88081_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 aw88081_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88081->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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88081_dev_get_prof_name(aw88081->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88081_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88081_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_set_profile_index(aw88081->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88081->lock);
+ return 0;
+ }
+
+ if (aw88081->aw_pa->status) {
+ aw88081_stop(aw88081);
+ aw88081_start(aw88081, AW88081_SYNC_START);
+ }
+
+ mutex_unlock(&aw88081->lock);
+
+ return 1;
+}
+
+static int aw88081_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88081_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88081->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;
+
+ aw88083_i2c_wen(aw88081, true);
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88081_dev_set_volume(aw88081->aw_pa, vol_desc->ctl_volume);
+ return 1;
+ }
+
+ aw88083_i2c_wen(aw88081, false);
+
+ return 0;
+}
+
+static int aw88081_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88081->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88081_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88081 *aw88081 = 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 (aw88081->aw_pa->fade_step != value) {
+ aw88081->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88081_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88081_SYSCTRL2_REG,
+ 0, AW88081_MUTE_VOL, 0, aw88081_volume_get,
+ aw88081_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88081_MUTE_VOL, 0,
+ aw88081_get_fade_step, aw88081_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_in_time, aw88081_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, 0,
+ aw88081_get_fade_out_time, aw88081_set_fade_out_time),
+ AW88081_PROFILE_EXT("Profile Set", aw88081_profile_info,
+ aw88081_profile_get, aw88081_profile_set),
+};
+
+static void aw88081_parse_channel_dt(struct aw88081 *aw88081)
+{
+ struct aw_device *aw_dev = aw88081->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88081_DEV_DEFAULT_CH;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88081->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+static int aw88081_init(struct aw88081 *aw88081, 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, AW88081_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+
+ switch (chip_id) {
+ case AW88081_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ case AW88083_CHIP_ID:
+ dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
+ break;
+ default:
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ aw88081->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = chip_id;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->fade_step = AW88081_VOLUME_STEP_DB;
+ aw_dev->volume_desc.mute_volume = AW88081_MUTE_VOL;
+ aw88081_parse_channel_dt(aw88081);
+
+ return 0;
+}
+
+static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88081->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, AW88081_ID_REG, AW88081_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88081_500_US;
+ aw_dev->fade_out_time = AW88081_500_US;
+ aw_dev->prof_cur = AW88081_INIT_PROFILE;
+ aw_dev->prof_index = AW88081_INIT_PROFILE;
+
+ ret = aw88081_dev_fw_update(aw88081);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw_dev->status = AW88081_DEV_PW_ON;
+ aw88081_stop(aw88081);
+
+ return 0;
+}
+
+static int aw88081_request_firmware_file(struct aw88081 *aw88081)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88081->aw_pa->fw_status = AW88081_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88081_ACF_FILE, aw88081->aw_pa->dev);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw88081->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88081_ACF_FILE, cont ? cont->size : 0);
+
+ aw88081->aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88081->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88081->aw_cfg->len = (int)cont->size;
+ memcpy(aw88081->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg);
+ if (ret)
+ return ret;
+
+ mutex_lock(&aw88081->lock);
+ ret = aw88081_dev_init(aw88081, aw88081->aw_cfg);
+ mutex_unlock(&aw88081->lock);
+
+ return ret;
+}
+
+static int aw88081_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 aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88081->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88081_start(aw88081, AW88081_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88081_stop(aw88081);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88081->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88081_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88081_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 aw88081_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88081_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88081->start_work, aw88081_startup_work);
+
+ ret = aw88081_request_firmware_file(aw88081);
+ if (ret)
+ dev_err(aw88081->aw_pa->dev, "%s: request firmware failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88081_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88081 *aw88081 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88081->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88081 = {
+ .probe = aw88081_codec_probe,
+ .remove = aw88081_codec_remove,
+ .dapm_widgets = aw88081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88081_dapm_widgets),
+ .dapm_routes = aw88081_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88081_audio_map),
+ .controls = aw88081_controls,
+ .num_controls = ARRAY_SIZE(aw88081_controls),
+};
+
+static const struct i2c_device_id aw88081_i2c_id[] = {
+ { AW88081_I2C_NAME, AW88081},
+ { AW88083_I2C_NAME, AW88083},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
+
+static int aw88081_i2c_probe(struct i2c_client *i2c)
+{
+ const struct regmap_config *regmap_config;
+ const struct i2c_device_id *id;
+ struct aw88081 *aw88081;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88081 = devm_kzalloc(&i2c->dev, sizeof(*aw88081), GFP_KERNEL);
+ if (!aw88081)
+ return -ENOMEM;
+
+ id = i2c_match_id(aw88081_i2c_id, i2c);
+ aw88081->devtype = id->driver_data;
+
+ mutex_init(&aw88081->lock);
+
+ i2c_set_clientdata(i2c, aw88081);
+
+ switch (aw88081->devtype) {
+ case AW88081:
+ regmap_config = &aw88081_regmap_config;
+ break;
+ case AW88083:
+ regmap_config = &aw88083_regmap_config;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ aw88081->regmap = devm_regmap_init_i2c(i2c, regmap_config);
+ if (IS_ERR(aw88081->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88081->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88081_init(aw88081, i2c, aw88081->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88081,
+ aw88081_dai, ARRAY_SIZE(aw88081_dai));
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id aw88081_of_match[] = {
+ { .compatible = "awinic,aw88081" },
+ { .compatible = "awinic,aw88083" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, aw88081_of_match);
+#endif
+
+static struct i2c_driver aw88081_i2c_driver = {
+ .driver = {
+ .name = AW88081_I2C_NAME,
+ .of_match_table = of_match_ptr(aw88081_of_match),
+ },
+ .probe = aw88081_i2c_probe,
+ .id_table = aw88081_i2c_id,
+};
+module_i2c_driver(aw88081_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88081 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88081.h b/sound/soc/codecs/aw88081.h
new file mode 100644
index 000000000000..7a4564270ab3
--- /dev/null
+++ b/sound/soc/codecs/aw88081.h
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88081.h -- AW88081 ALSA SoC Audio driver
+//
+// Copyright (c) 2024 awinic Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88081_H__
+#define __AW88081_H__
+
+#define AW88081_ID_REG (0x00)
+#define AW88081_SYSST_REG (0x01)
+#define AW88081_SYSINT_REG (0x02)
+#define AW88081_SYSINTM_REG (0x03)
+#define AW88081_SYSCTRL_REG (0x04)
+#define AW88081_SYSCTRL2_REG (0x05)
+#define AW88081_I2SCTRL1_REG (0x06)
+#define AW88081_I2SCTRL2_REG (0x07)
+#define AW88081_I2SCTRL3_REG (0x08)
+#define AW88081_DACCFG1_REG (0x09)
+#define AW88081_DACCFG2_REG (0x0A)
+#define AW88081_DACCFG3_REG (0x0B)
+#define AW88081_DACCFG4_REG (0x0C)
+#define AW88081_DACCFG5_REG (0x0D)
+#define AW88081_DACCFG6_REG (0x0E)
+#define AW88081_DACCFG7_REG (0x11)
+#define AW88081_PWMCTRL1_REG (0x13)
+#define AW88081_PWMCTRL2_REG (0x14)
+#define AW88081_PWMCTRL3_REG (0x15)
+#define AW88081_PWMCTRL4_REG (0x16)
+#define AW88081_I2SCFG1_REG (0x17)
+#define AW88081_DBGCTRL_REG (0x18)
+#define AW88081_PDMCTRL_REG (0x19)
+#define AW88081_DACST_REG (0x20)
+#define AW88081_PATTERNST_REG (0x21)
+#define AW88081_I2SINT_REG (0x26)
+#define AW88081_I2SCAPCNT_REG (0x27)
+#define AW88081_ANASTA1_REG (0x28)
+#define AW88081_ANASTA2_REG (0x29)
+#define AW88081_ANASTA3_REG (0x2A)
+#define AW88081_VBAT_REG (0x21)
+#define AW88081_TEMP_REG (0x22)
+#define AW88081_PVDD_REG (0x23)
+#define AW88081_ISNDAT_REG (0x24)
+#define AW88081_VSNDAT_REG (0x25)
+#define AW88081_DSMCFG1_REG (0x30)
+#define AW88081_DSMCFG2_REG (0x31)
+#define AW88081_DSMCFG3_REG (0x32)
+#define AW88081_DSMCFG4_REG (0x33)
+#define AW88081_DSMCFG5_REG (0x34)
+#define AW88081_DSMCFG6_REG (0x35)
+#define AW88081_DSMCFG7_REG (0x36)
+#define AW88081_DSMCFG8_REG (0x37)
+#define AW88081_TESTIN_REG (0x38)
+#define AW88081_TESTOUT_REG (0x39)
+#define AW88081_BOPCTRL1_REG (0x40)
+#define AW88081_BOPCTRL2_REG (0x41)
+#define AW88081_BOPCTRL3_REG (0x42)
+#define AW88081_BOPSTA_REG (0x43)
+#define AW88081_PLLCTRL1_REG (0x54)
+#define AW88081_PLLCTRL2_REG (0x55)
+#define AW88081_PLLCTRL3_REG (0x56)
+#define AW88081_CDACTRL1_REG (0x57)
+#define AW88081_CDACTRL2_REG (0x58)
+#define AW88081_CDACTRL3_REG (0x59)
+#define AW88081_DITHERCFG1_REG (0x5A)
+#define AW88081_DITHERCFG2_REG (0x5B)
+#define AW88081_DITHERCFG3_REG (0x5C)
+#define AW88081_TM_REG (0x6E)
+#define AW88081_TM2_REG (0x6F)
+#define AW88081_TESTCTRL1_REG (0x70)
+#define AW88081_TESTCTRL2_REG (0x71)
+
+#define AW88081_REG_MAX (0x72)
+
+#define AW88081_UVLS_START_BIT (14)
+#define AW88081_UVLS_UVLO (1)
+#define AW88081_UVLS_UVLO_VALUE \
+ (AW88081_UVLS_UVLO << AW88081_UVLS_START_BIT)
+
+#define AW88081_SWS_START_BIT (8)
+#define AW88081_SWS_SWITCHING (1)
+#define AW88081_SWS_SWITCHING_VALUE \
+ (AW88081_SWS_SWITCHING << AW88081_SWS_START_BIT)
+
+#define AW88081_NOCLKS_START_BIT (5)
+#define AW88081_NOCLKS_NO_CLOCK (1)
+#define AW88081_NOCLKS_NO_CLOCK_VALUE \
+ (AW88081_NOCLKS_NO_CLOCK << AW88081_NOCLKS_START_BIT)
+
+#define AW88081_CLKS_START_BIT (4)
+#define AW88081_CLKS_STABLE (1)
+#define AW88081_CLKS_STABLE_VALUE \
+ (AW88081_CLKS_STABLE << AW88081_CLKS_START_BIT)
+
+#define AW88081_OCDS_START_BIT (3)
+#define AW88081_OCDS_OC (1)
+#define AW88081_OCDS_OC_VALUE \
+ (AW88081_OCDS_OC << AW88081_OCDS_START_BIT)
+
+#define AW88081_OTHS_START_BIT (1)
+#define AW88081_OTHS_OT (1)
+#define AW88081_OTHS_OT_VALUE \
+ (AW88081_OTHS_OT << AW88081_OTHS_START_BIT)
+
+#define AW88081_PLLS_START_BIT (0)
+#define AW88081_PLLS_LOCKED (1)
+#define AW88081_PLLS_LOCKED_VALUE \
+ (AW88081_PLLS_LOCKED << AW88081_PLLS_START_BIT)
+
+#define AW88081_BIT_PLL_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_BIT_SYSST_CHECK_MASK \
+ (~(AW88081_UVLS_UVLO_VALUE | \
+ AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_NOCLKS_NO_CLOCK_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_OCDS_OC_VALUE | \
+ AW88081_OTHS_OT_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE))
+
+#define AW88081_NO_SWS_SYSST_CHECK \
+ (AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_SWS_SYSST_CHECK \
+ (AW88081_SWS_SWITCHING_VALUE | \
+ AW88081_CLKS_STABLE_VALUE | \
+ AW88081_PLLS_LOCKED_VALUE)
+
+#define AW88081_ULS_HMUTE_START_BIT (14)
+#define AW88081_ULS_HMUTE_BITS_LEN (1)
+#define AW88081_ULS_HMUTE_MASK \
+ (~(((1<<AW88081_ULS_HMUTE_BITS_LEN)-1) << AW88081_ULS_HMUTE_START_BIT))
+
+#define AW88081_ULS_HMUTE_DISABLE (0)
+#define AW88081_ULS_HMUTE_DISABLE_VALUE \
+ (AW88081_ULS_HMUTE_DISABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_ULS_HMUTE_ENABLE (1)
+#define AW88081_ULS_HMUTE_ENABLE_VALUE \
+ (AW88081_ULS_HMUTE_ENABLE << AW88081_ULS_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_START_BIT (8)
+#define AW88081_HMUTE_BITS_LEN (1)
+#define AW88081_HMUTE_MASK \
+ (~(((1<<AW88081_HMUTE_BITS_LEN)-1) << AW88081_HMUTE_START_BIT))
+
+#define AW88081_HMUTE_DISABLE (0)
+#define AW88081_HMUTE_DISABLE_VALUE \
+ (AW88081_HMUTE_DISABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_HMUTE_ENABLE (1)
+#define AW88081_HMUTE_ENABLE_VALUE \
+ (AW88081_HMUTE_ENABLE << AW88081_HMUTE_START_BIT)
+
+#define AW88081_EN_PA_START_BIT (1)
+#define AW88081_EN_PA_BITS_LEN (1)
+#define AW88081_EN_PA_MASK \
+ (~(((1<<AW88081_EN_PA_BITS_LEN)-1) << AW88081_EN_PA_START_BIT))
+
+#define AW88081_EN_PA_WORKING (1)
+#define AW88081_EN_PA_WORKING_VALUE \
+ (AW88081_EN_PA_WORKING << AW88081_EN_PA_START_BIT)
+
+#define AW88081_EN_PA_POWER_DOWN (0)
+#define AW88081_EN_PA_POWER_DOWN_VALUE \
+ (AW88081_EN_PA_POWER_DOWN << AW88081_EN_PA_START_BIT)
+
+#define AW88081_PWDN_START_BIT (0)
+#define AW88081_PWDN_BITS_LEN (1)
+#define AW88081_PWDN_MASK \
+ (~(((1<<AW88081_PWDN_BITS_LEN)-1) << AW88081_PWDN_START_BIT))
+
+#define AW88081_PWDN_WORKING (0)
+#define AW88081_PWDN_WORKING_VALUE \
+ (AW88081_PWDN_WORKING << AW88081_PWDN_START_BIT)
+
+#define AW88081_PWDN_POWER_DOWN (1)
+#define AW88081_PWDN_POWER_DOWN_VALUE \
+ (AW88081_PWDN_POWER_DOWN << AW88081_PWDN_START_BIT)
+
+#define AW88081_VOL_START_BIT (0)
+#define AW88081_VOL_BITS_LEN (10)
+#define AW88081_VOL_MASK \
+ (~(((1<<AW88081_VOL_BITS_LEN)-1) << AW88081_VOL_START_BIT))
+
+#define AW88081_VOLUME_STEP_DB (64)
+#define AW88081_MUTE_VOL (1023)
+
+#define AW88081_I2STXEN_START_BIT (6)
+#define AW88081_I2STXEN_BITS_LEN (1)
+#define AW88081_I2STXEN_MASK \
+ (~(((1<<AW88081_I2STXEN_BITS_LEN)-1) << AW88081_I2STXEN_START_BIT))
+
+#define AW88081_I2STXEN_DISABLE (0)
+#define AW88081_I2STXEN_DISABLE_VALUE \
+ (AW88081_I2STXEN_DISABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_I2STXEN_ENABLE (1)
+#define AW88081_I2STXEN_ENABLE_VALUE \
+ (AW88081_I2STXEN_ENABLE << AW88081_I2STXEN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_START_BIT (13)
+#define AW88081_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88081_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88081_NOISE_GATE_EN_BITS_LEN)-1) << AW88081_NOISE_GATE_EN_START_BIT))
+
+#define AW88081_NOISE_GATE_EN_DISABLE (0)
+#define AW88081_NOISE_GATE_EN_DISABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_DISABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_NOISE_GATE_EN_ENABLE (1)
+#define AW88081_NOISE_GATE_EN_ENABLE_VALUE \
+ (AW88081_NOISE_GATE_EN_ENABLE << AW88081_NOISE_GATE_EN_START_BIT)
+
+#define AW88081_CCO_MUX_START_BIT (13)
+#define AW88081_CCO_MUX_BITS_LEN (1)
+#define AW88081_CCO_MUX_MASK \
+ (~(((1<<AW88081_CCO_MUX_BITS_LEN)-1) << AW88081_CCO_MUX_START_BIT))
+
+#define AW88081_CCO_MUX_DIVIDED (0)
+#define AW88081_CCO_MUX_DIVIDED_VALUE \
+ (AW88081_CCO_MUX_DIVIDED << AW88081_CCO_MUX_START_BIT)
+
+#define AW88081_CCO_MUX_BYPASS (1)
+#define AW88081_CCO_MUX_BYPASS_VALUE \
+ (AW88081_CCO_MUX_BYPASS << AW88081_CCO_MUX_START_BIT)
+
+#define AW88083_I2C_WEN_START_BIT (14)
+#define AW88083_I2C_WEN_BITS_LEN (2)
+#define AW88083_I2C_WEN_MASK \
+ (~(((1<<AW88083_I2C_WEN_BITS_LEN)-1) << AW88083_I2C_WEN_START_BIT))
+
+#define AW88083_I2C_WEN_DISABLE (0)
+#define AW88083_I2C_WEN_DISABLE_VALUE \
+ (AW88083_I2C_WEN_DISABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_I2C_WEN_ENABLE (2)
+#define AW88083_I2C_WEN_ENABLE_VALUE \
+ (AW88083_I2C_WEN_ENABLE << AW88083_I2C_WEN_START_BIT)
+
+#define AW88083_PLL_PD_START_BIT (2)
+#define AW88083_PLL_PD_BITS_LEN (1)
+#define AW88083_PLL_PD_MASK \
+ (~(((1<<AW88083_PLL_PD_BITS_LEN)-1) << AW88083_PLL_PD_START_BIT))
+
+#define AW88083_PLL_PD_POWER_DOWN (1)
+#define AW88083_PLL_PD_POWER_DOWN_VALUE \
+ (AW88083_PLL_PD_POWER_DOWN << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_PLL_PD_WORKING (0)
+#define AW88083_PLL_PD_WORKING_VALUE \
+ (AW88083_PLL_PD_WORKING << AW88083_PLL_PD_START_BIT)
+
+#define AW88083_AMPPD_START_BIT (1)
+#define AW88083_AMPPD_BITS_LEN (1)
+#define AW88083_AMPPD_MASK \
+ (~(((1<<AW88083_AMPPD_BITS_LEN)-1) << AW88083_AMPPD_START_BIT))
+
+#define AW88083_AMPPD_WORKING (0)
+#define AW88083_AMPPD_WORKING_VALUE \
+ (AW88083_AMPPD_WORKING << AW88083_AMPPD_START_BIT)
+
+#define AW88083_AMPPD_POWER_DOWN (1)
+#define AW88083_AMPPD_POWER_DOWN_VALUE \
+ (AW88083_AMPPD_POWER_DOWN << AW88083_AMPPD_START_BIT)
+
+#define AW88083_REG_MAX (0x7D)
+#define AW88083_I2C_NAME "aw88083"
+#define AW88083_CHIP_ID 0x2407
+
+#define AW88081_START_RETRIES (5)
+#define AW88081_START_WORK_DELAY_MS (0)
+
+#define AW88081_I2C_NAME "aw88081"
+#define AW88081_CHIP_ID 0x2116
+
+#define AW88081_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88081_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+
+#define AW88081_DEV_DEFAULT_CH (0)
+#define AW88081_ACF_FILE "aw88081_acf.bin"
+#define AW88081_DEV_SYSST_CHECK_MAX (10)
+#define AW88081_SOFT_RESET_VALUE (0x55aa)
+
+#define AW88081_INIT_PROFILE (0)
+
+#define AW88081_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 {
+ AW88081_SYNC_START = 0,
+ AW88081_ASYNC_START,
+};
+
+enum {
+ AW88081_500_US = 500,
+ AW88081_1000_US = 1000,
+ AW88081_2000_US = 2000,
+ AW88081_5000_US = 5000,
+};
+
+enum {
+ AW88081_DEV_PW_OFF = 0,
+ AW88081_DEV_PW_ON,
+};
+
+enum {
+ AW88081_DEV_FW_FAILED = 0,
+ AW88081_DEV_FW_OK,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c
new file mode 100644
index 000000000000..28f62b991ef2
--- /dev/null
+++ b/sound/soc/codecs/aw88166.c
@@ -0,0 +1,1928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.c -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88166.h"
+#include "aw88395/aw88395_device.h"
+
+struct aw88166 {
+ 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;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int re_init_val;
+ unsigned int dither_st;
+ bool phase_sync;
+};
+
+static const struct regmap_config aw88166_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88166_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+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, AW88166_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, AW88166_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_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, AW88166_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, AW88166_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, AW88166_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, AW88166_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, AW88166_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 AW88166_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 failed", (u32)dsp_addr);
+ break;
+ case AW88166_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] 32-bit failed", (u32)dsp_addr);
+ 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, AW88166_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. ret=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ 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, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_PWDN_MASK, AW88166_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88166_SYSINT_REG, &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_dbg(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, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88166_BIT_PLL_CHECK) != AW88166_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 < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_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, AW88166_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88166_CCO_MUX_MASK);
+ if (reg_val == AW88166_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, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88166_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(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88166_PLLCTRL2_REG,
+ ~AW88166_CCO_MUX_MASK, AW88166_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88166_2000_US, AW88166_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 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88166_NOISE_GATE_EN_MASK))
+ check_val = AW88166_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88166_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88166_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88166_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88166_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_AMPPD_MASK, AW88166_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_DSPBY_MASK, AW88166_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88166_dev_get_icalk(struct aw88166 *aw88166, int16_t *icalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_isn_geslp, ef_isn_h5bits;
+ uint16_t icalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_geslp = (efrm_reg_val & (~AW88166_EF_ISN_GESLP_MASK)) >>
+ AW88166_EF_ISN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_isn_h5bits = (efrl_reg_val & (~AW88166_EF_ISN_H5BITS_MASK)) >>
+ AW88166_EF_ISN_H5BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ icalk_val = ef_isn_geslp & (ef_isn_h5bits | AW88166_EF_ISN_H5BITS_SIGN_MASK);
+ else
+ icalk_val = ef_isn_geslp | (ef_isn_h5bits & (~AW88166_EF_ISN_H5BITS_SIGN_MASK));
+
+ if (icalk_val & (~AW88166_ICALK_SIGN_MASK))
+ icalk_val = icalk_val | AW88166_ICALK_NEG_MASK;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_get_vcalk(struct aw88166 *aw88166, int16_t *vcalk)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int efrm_reg_val, efrl_reg_val;
+ uint16_t ef_vsn_geslp, ef_vsn_h3bits;
+ uint16_t vcalk_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRM2_REG, &efrm_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_geslp = (efrm_reg_val & (~AW88166_EF_VSN_GESLP_MASK)) >>
+ AW88166_EF_VSN_GESLP_SHIFT;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_EFRL_REG, &efrl_reg_val);
+ if (ret)
+ return ret;
+
+ ef_vsn_h3bits = (efrl_reg_val & (~AW88166_EF_VSN_H3BITS_MASK)) >>
+ AW88166_EF_VSN_H3BITS_SHIFT;
+
+ if (aw88166->check_val == AW_EF_AND_CHECK)
+ vcalk_val = ef_vsn_geslp & (ef_vsn_h3bits | AW88166_EF_VSN_H3BITS_SIGN_MASK);
+ else
+ vcalk_val = ef_vsn_geslp | (ef_vsn_h3bits & (~AW88166_EF_VSN_H3BITS_SIGN_MASK));
+
+ if (vcalk_val & (~AW88166_VCALK_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88166_VCALK_NEG_MASK;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88166_dev_set_vcalb(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ unsigned int reg_val;
+ int ret;
+
+ ret = aw88166_dev_get_icalk(aw88166, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+ ical_k = icalk * AW88166_ICABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ ret = aw88166_dev_get_vcalk(aw88166, &vcalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get vbcalk failed\n");
+ return ret;
+ }
+ vcal_k = vcalk * AW88166_VCABLK_FACTOR + AW88166_CABL_BASE_VALUE;
+
+ vcalb = AW88166_VCALB_ACCURACY * AW88166_VSCAL_FACTOR /
+ AW88166_ISCAL_FACTOR * ical_k / vcal_k * aw88166->vcalb_init_val;
+
+ vcalb = vcalb >> AW88166_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int aw_dev_init_vcalb_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPVCALB_REG, aw88166->vcalb_init_val);
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_DSPVCALB_REG, &aw88166->vcalb_init_val);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int aw_dev_init_re_update(struct aw88166 *aw88166, int flag)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ unsigned int re_temp_h, re_temp_l;
+ int ret;
+
+ switch (flag) {
+ case AW88166_RECOVERY_SEC_DATA:
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, aw88166->re_init_val >> 16);
+ if (ret)
+ return ret;
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG,
+ (uint16_t)aw88166->re_init_val);
+ if (ret)
+ return ret;
+ break;
+ case AW88166_RECORD_SEC_DATA:
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR1_REG, &re_temp_h);
+ if (ret)
+ return ret;
+ ret = regmap_read(aw_dev->regmap, AW88166_ACR2_REG, &re_temp_l);
+ if (ret)
+ return ret;
+ aw88166->re_init_val = (re_temp_h << 16) + re_temp_l;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported type:%d\n", flag);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void aw_dev_backup_sec_record(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECORD_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static void aw_dev_backup_sec_recovery(struct aw88166 *aw88166)
+{
+ aw_dev_init_vcalb_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+ aw_dev_init_re_update(aw88166, AW88166_RECOVERY_SEC_DATA);
+}
+
+static int 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);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88166_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88166_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88166_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88166_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88166_CALI_RE_HBITS_MASK)) >> AW88166_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88166_CALI_RE_LBITS_MASK)) >> AW88166_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CODE_EN_MASK, AW88166_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x\n",
+ __func__, check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88166_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88166_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88166_CRC_CHECK_BITS_MASK)) >> AW88166_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_CRCCTRL_REG,
+ ~AW88166_CRC_CFG_EN_MASK, AW88166_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88166_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x\n",
+ check_val, AW88166_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88166_CRCCTRL_REG, aw88166->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88166_I2SCFG1_REG,
+ ~AW88166_RAM_CG_BYP_MASK, AW88166_RAM_CG_BYP_WORK_VALUE);
+ 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, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_I2SCTRL3_REG,
+ ~AW88166_I2STXEN_MASK, AW88166_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88166_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88166_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88166_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88166_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ for (i = 0; i < AW88166_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(AW88166_2000_US, AW88166_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88166_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88166_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88166_VOL_START_BIT) | (reg_value & AW88166_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88166_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+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 (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88166_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88166_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 != AW88166_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88166_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88166_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88166_SYSCTRL_REG,
+ ~AW88166_HMUTE_MASK, AW88166_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88166_dev_set_dither(struct aw88166 *aw88166, bool dither)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_DITHER_EN_MASK, AW88166_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88166_dev_start(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88166_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88166_dev_set_dither(aw88166, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_2000_US, AW88166_2000_US + 10);
+
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw_dev_amppd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed\n");
+ goto sysst_check_fail;
+ }
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK) {
+ aw_dev_backup_sec_recovery(aw88166);
+ ret = aw_dev_hw_crc_check(aw88166);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed\n");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw88166_dev_set_vcalb(aw88166);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ ret = aw_dev_dsp_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status check failed\n");
+ 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);
+
+ if (aw88166->dither_st == AW88166_DITHER_EN_ENABLE_VALUE)
+ aw88166_dev_set_dither(aw88166, true);
+
+ /* close mute */
+ aw88166_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88166_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+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 = AW88166_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88166_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88166_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+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, AW88166_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88166_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error\n");
+ return ret;
+ }
+
+ cali_desc->ra = AW88166_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88166_DSP_RE_SHIFT);
+
+ 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\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ 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\n");
+ return -EINVAL;
+ }
+
+ aw_dev->dsp_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88166_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_ROM_CHECK_ADDR, &reg_val);
+ if (reg_val != AW88166_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]\n",
+ reg_val, AW88166_DSP_ROM_CHECK_DATA);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88166_DSP_CFG_ADDR, AW88166_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88166_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88166_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]\n",
+ reg_val, AW88166_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88166_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed\n");
+ break;
+ case AW88166_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88166_DBGCTRL_REG,
+ ~AW88166_MEM_CLKSEL_MASK,
+ AW88166_MEM_CLKSEL_OSCCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select OSC failed\n");
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown memclk config, flag=0x%x\n", flag);
+ break;
+ }
+}
+
+static int aw_dev_update_reg_container(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported\n", 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 == AW88166_DSPVCALB_REG) {
+ aw88166->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL_REG) {
+ if (reg_val & (~AW88166_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88166_DEV_DSP_WORK;
+
+ reg_val &= (AW88166_HMUTE_MASK | AW88166_PWDN_MASK |
+ AW88166_DSPBY_MASK);
+ reg_val |= (AW88166_HMUTE_ENABLE_VALUE | AW88166_PWDN_POWER_DOWN_VALUE |
+ AW88166_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88166_I2SCTRL3_REG) {
+ reg_val &= AW88166_I2STXEN_MASK;
+ reg_val |= AW88166_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88166_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88166_VOL_MASK)) >>
+ AW88166_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88166_DBGCTRL_REG) {
+ if ((reg_val & (~AW88166_EF_DBMD_MASK)) == AW88166_EF_DBMD_OR_VALUE)
+ aw88166->check_val = AW_EF_OR_CHECK;
+ else
+ aw88166->check_val = AW_EF_AND_CHECK;
+
+ aw88166->dither_st = reg_val & (~AW88166_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88166_ACR1_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val << 16;
+ continue;
+ }
+
+ if (reg_addr == AW88166_ACR2_REG) {
+ aw88166->re_init_val |= (uint32_t)reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88166_CRCCTRL_REG)
+ aw88166->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88166 *aw88166,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88166->aw_pa->dev, "reg data is null or len is 0\n");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88166, data, len);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "reg update failed\n");
+
+ return ret;
+}
+
+static int aw88166_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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]\n",
+ index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88166_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 aw88166_dev_fw_update(struct aw88166 *aw88166, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ 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 == AW88166_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88166_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error\n", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88166_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88166_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(aw88166, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed\n");
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88166_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_recovery(aw88166);
+
+ if (up_dsp_fw_en) {
+ 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\n");
+ 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\n");
+ goto error;
+ }
+
+ aw_dev_backup_sec_record(aw88166);
+
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88166_start_pa(struct aw88166 *aw88166)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88166_START_RETRIES; i++) {
+ ret = aw88166_dev_start(aw88166);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "aw88166 device start failed. retry = %d", i);
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88166->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88166_startup_work(struct work_struct *work)
+{
+ struct aw88166 *aw88166 =
+ container_of(work, struct aw88166, start_work.work);
+
+ mutex_lock(&aw88166->lock);
+ aw88166_start_pa(aw88166);
+ mutex_unlock(&aw88166->lock);
+}
+
+static void aw88166_start(struct aw88166 *aw88166, bool sync_start)
+{
+ int ret;
+
+ if (aw88166->aw_pa->fw_status != AW88166_DEV_FW_OK)
+ return;
+
+ if (aw88166->aw_pa->status == AW88166_DEV_PW_ON)
+ return;
+
+ ret = aw88166_dev_fw_update(aw88166, AW88166_DSP_FW_UPDATE_OFF, aw88166->phase_sync);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "fw update failed\n");
+ return;
+ }
+
+ if (sync_start == AW88166_SYNC_START)
+ aw88166_start_pa(aw88166);
+ else
+ queue_delayed_work(system_wq,
+ &aw88166->start_work,
+ AW88166_START_WORK_DELAY_MS);
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+ if (reg_val & AW88166_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x\n", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88166_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;
+
+ if (aw_dev->status == AW88166_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88166_DEV_PW_OFF;
+
+ aw88166_dev_mute(aw_dev, true);
+ usleep_range(AW88166_4000_US, AW88166_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88166_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88166_dai[] = {
+ {
+ .name = "aw88166-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88166_RATES,
+ .formats = AW88166_FORMATS,
+ },
+ },
+};
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 aw88166_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;
+}
+
+static int aw88166_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88166->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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88166_dev_get_prof_name(aw88166->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88166_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88166_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88166->lock);
+ ret = aw88166_dev_set_profile_index(aw88166->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88166->lock);
+ return 0;
+ }
+
+ if (aw88166->aw_pa->status) {
+ aw88166_stop(aw88166->aw_pa);
+ aw88166_start(aw88166, AW88166_SYNC_START);
+ }
+
+ mutex_unlock(&aw88166->lock);
+
+ return 1;
+}
+
+static int aw88166_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88166_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88166->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;
+ aw_dev_set_volume(aw88166->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88166->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88166_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = 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 (aw88166->aw_pa->fade_step != value) {
+ aw88166->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88166_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88166->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88166_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88166->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 int aw88166_dev_init(struct aw88166 *aw88166, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88166->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\n");
+ return -EINVAL;
+ }
+ aw_dev->fade_in_time = AW88166_1000_US / 10;
+ aw_dev->fade_out_time = AW88166_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 = aw88166_dev_fw_update(aw88166, AW88166_FORCE_UPDATE_ON, AW88166_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88166_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88166_request_firmware_file(struct aw88166 *aw88166)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88166->aw_pa->fw_status = AW88166_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88166_ACF_FILE, aw88166->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "request [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88166->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88166_ACF_FILE, cont ? cont->size : 0);
+
+ aw88166->aw_cfg = devm_kzalloc(aw88166->aw_pa->dev,
+ struct_size(aw88166->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88166->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88166->aw_cfg->len = (int)cont->size;
+ memcpy(aw88166->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88166->aw_pa, aw88166->aw_cfg);
+ if (ret) {
+ dev_err(aw88166->aw_pa->dev, "load [%s] failed!\n", AW88166_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88166->lock);
+ /* aw device init */
+ ret = aw88166_dev_init(aw88166, aw88166->aw_cfg);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "dev init failed\n");
+ mutex_unlock(&aw88166->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88166_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88166_SYSCTRL2_REG,
+ 6, AW88166_MUTE_VOL, 0, aw88166_volume_get,
+ aw88166_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88166_MUTE_VOL, 0,
+ aw88166_get_fade_step, aw88166_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_in_time, aw88166_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88166_get_fade_out_time, aw88166_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88166_CALI_RE_MAX, 0,
+ aw88166_re_get, aw88166_re_set),
+ AW88166_PROFILE_EXT("AW88166 Profile Set", aw88166_profile_info,
+ aw88166_profile_get, aw88166_profile_set),
+};
+
+static int aw88166_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 aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88166->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88166_start(aw88166, AW88166_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88166_stop(aw88166->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88166->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88166_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, SND_SOC_NOPM, 0, 0,
+ aw88166_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 aw88166_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88166_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88166->start_work, aw88166_startup_work);
+
+ ret = aw88166_request_firmware_file(aw88166);
+ if (ret)
+ dev_err(aw88166->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88166_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88166 *aw88166 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88166->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88166 = {
+ .probe = aw88166_codec_probe,
+ .remove = aw88166_codec_remove,
+ .dapm_widgets = aw88166_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88166_dapm_widgets),
+ .dapm_routes = aw88166_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88166_audio_map),
+ .controls = aw88166_controls,
+ .num_controls = ARRAY_SIZE(aw88166_controls),
+};
+
+static void aw88166_hw_reset(struct aw88166 *aw88166)
+{
+ if (aw88166->reset_gpio) {
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 1);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ gpiod_set_value_cansleep(aw88166->reset_gpio, 0);
+ usleep_range(AW88166_1000_US, AW88166_1000_US + 10);
+ }
+}
+
+static void aw88166_parse_channel_dt(struct aw88166 *aw88166)
+{
+ struct aw_device *aw_dev = aw88166->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+ aw88166->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+}
+
+static int aw88166_init(struct aw88166 *aw88166, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88166_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d\n", __func__, ret);
+ return ret;
+ }
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88166->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = 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 = AW88166_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88166_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88166_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88166_VOL_DEFAULT_VALUE;
+
+ aw88166_parse_channel_dt(aw88166);
+
+ return 0;
+}
+
+static int aw88166_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88166 *aw88166;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed\n");
+
+ aw88166 = devm_kzalloc(&i2c->dev, sizeof(*aw88166), GFP_KERNEL);
+ if (!aw88166)
+ return -ENOMEM;
+
+ mutex_init(&aw88166->lock);
+
+ i2c_set_clientdata(i2c, aw88166);
+
+ aw88166->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88166->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->reset_gpio),
+ "reset gpio not defined\n");
+ aw88166_hw_reset(aw88166);
+
+ aw88166->regmap = devm_regmap_init_i2c(i2c, &aw88166_remap_config);
+ if (IS_ERR(aw88166->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88166->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88166_init(aw88166, i2c, aw88166->regmap);
+ if (ret)
+ return ret;
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88166,
+ aw88166_dai, ARRAY_SIZE(aw88166_dai));
+}
+
+static const struct i2c_device_id aw88166_i2c_id[] = {
+ { AW88166_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id);
+
+static struct i2c_driver aw88166_i2c_driver = {
+ .driver = {
+ .name = AW88166_I2C_NAME,
+ },
+ .probe = aw88166_i2c_probe,
+ .id_table = aw88166_i2c_id,
+};
+module_i2c_driver(aw88166_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88166 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88166.h b/sound/soc/codecs/aw88166.h
new file mode 100644
index 000000000000..3a53ba0ac625
--- /dev/null
+++ b/sound/soc/codecs/aw88166.h
@@ -0,0 +1,534 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88166.h -- ALSA SoC AW88166 codec support
+//
+// Copyright (c) 2025 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88166_H__
+#define __AW88166_H__
+
+/* registers list */
+#define AW88166_ID_REG (0x00)
+#define AW88166_SYSST_REG (0x01)
+#define AW88166_SYSINT_REG (0x02)
+#define AW88166_SYSINTM_REG (0x03)
+#define AW88166_SYSCTRL_REG (0x04)
+#define AW88166_SYSCTRL2_REG (0x05)
+#define AW88166_I2SCTRL1_REG (0x06)
+#define AW88166_I2SCTRL2_REG (0x07)
+#define AW88166_I2SCTRL3_REG (0x08)
+#define AW88166_DACCFG1_REG (0x09)
+#define AW88166_DACCFG2_REG (0x0A)
+#define AW88166_DACCFG3_REG (0x0B)
+#define AW88166_DACCFG4_REG (0x0C)
+#define AW88166_DACCFG5_REG (0x0D)
+#define AW88166_DACCFG6_REG (0x0E)
+#define AW88166_DACCFG7_REG (0x0F)
+#define AW88166_MPDCFG1_REG (0x10)
+#define AW88166_MPDCFG2_REG (0x11)
+#define AW88166_MPDCFG3_REG (0x12)
+#define AW88166_MPDCFG4_REG (0x13)
+#define AW88166_PWMCTRL1_REG (0x14)
+#define AW88166_PWMCTRL2_REG (0x15)
+#define AW88166_PWMCTRL3_REG (0x16)
+#define AW88166_I2SCFG1_REG (0x17)
+#define AW88166_DBGCTRL_REG (0x18)
+#define AW88166_HAGCST_REG (0x20)
+#define AW88166_VBAT_REG (0x21)
+#define AW88166_TEMP_REG (0x22)
+#define AW88166_PVDD_REG (0x23)
+#define AW88166_ISNDAT_REG (0x24)
+#define AW88166_I2SINT_REG (0x25)
+#define AW88166_I2SCAPCNT_REG (0x26)
+#define AW88166_ANASTA1_REG (0x27)
+#define AW88166_ANASTA2_REG (0x28)
+#define AW88166_ANASTA3_REG (0x29)
+#define AW88166_TESTDET_REG (0x2A)
+#define AW88166_TESTIN_REG (0x38)
+#define AW88166_TESTOUT_REG (0x39)
+#define AW88166_MEMTEST_REG (0x3A)
+#define AW88166_DSPMADD_REG (0x40)
+#define AW88166_DSPMDAT_REG (0x41)
+#define AW88166_WDT_REG (0x42)
+#define AW88166_ACR1_REG (0x43)
+#define AW88166_ACR2_REG (0x44)
+#define AW88166_ASR1_REG (0x45)
+#define AW88166_ASR2_REG (0x46)
+#define AW88166_DSPCFG_REG (0x47)
+#define AW88166_ASR3_REG (0x48)
+#define AW88166_ASR4_REG (0x49)
+#define AW88166_DSPVCALB_REG (0x4A)
+#define AW88166_CRCCTRL_REG (0x4B)
+#define AW88166_DSPDBG1_REG (0x4C)
+#define AW88166_DSPDBG2_REG (0x4D)
+#define AW88166_DSPDBG3_REG (0x4E)
+#define AW88166_ISNCTRL1_REG (0x50)
+#define AW88166_PLLCTRL1_REG (0x51)
+#define AW88166_PLLCTRL2_REG (0x52)
+#define AW88166_PLLCTRL3_REG (0x53)
+#define AW88166_CDACTRL1_REG (0x54)
+#define AW88166_CDACTRL2_REG (0x55)
+#define AW88166_CDACTRL3_REG (0x56)
+#define AW88166_SADCCTRL1_REG (0x57)
+#define AW88166_SADCCTRL2_REG (0x58)
+#define AW88166_BOPCTRL1_REG (0x59)
+#define AW88166_BOPCTRL2_REG (0x5A)
+#define AW88166_BOPCTRL3_REG (0x5B)
+#define AW88166_BOPCTRL4_REG (0x5C)
+#define AW88166_BOPCTRL5_REG (0x5D)
+#define AW88166_BOPCTRL6_REG (0x5E)
+#define AW88166_BOPCTRL7_REG (0x5F)
+#define AW88166_BSTCTRL1_REG (0x60)
+#define AW88166_BSTCTRL2_REG (0x61)
+#define AW88166_BSTCTRL3_REG (0x62)
+#define AW88166_BSTCTRL4_REG (0x63)
+#define AW88166_BSTCTRL5_REG (0x64)
+#define AW88166_BSTCTRL6_REG (0x65)
+#define AW88166_DSMCFG1_REG (0x66)
+#define AW88166_DSMCFG2_REG (0x67)
+#define AW88166_DSMCFG3_REG (0x68)
+#define AW88166_DSMCFG4_REG (0x69)
+#define AW88166_DSMCFG5_REG (0x6A)
+#define AW88166_DSMCFG6_REG (0x6B)
+#define AW88166_DSMCFG7_REG (0x6C)
+#define AW88166_DSMCFG8_REG (0x6D)
+#define AW88166_TESTCTRL1_REG (0x70)
+#define AW88166_TESTCTRL2_REG (0x71)
+#define AW88166_EFCTRL1_REG (0x72)
+#define AW88166_EFCTRL2_REG (0x73)
+#define AW88166_EFWH_REG (0x74)
+#define AW88166_EFWM2_REG (0x75)
+#define AW88166_EFWM1_REG (0x76)
+#define AW88166_EFRH_REG (0x77)
+#define AW88166_EFRM2_REG (0x78)
+#define AW88166_EFRM1_REG (0x79)
+#define AW88166_EFRL_REG (0x7A)
+#define AW88166_TM_REG (0x7C)
+#define AW88166_TM2_REG (0x7D)
+
+#define AW88166_REG_MAX (0x7E)
+#define AW88166_MUTE_VOL (1023)
+
+#define AW88166_DSP_CFG_ADDR (0x9B00)
+#define AW88166_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88166_DSP_FW_ADDR (0x8980)
+#define AW88166_DSP_ROM_CHECK_ADDR (0x1F40)
+
+#define AW88166_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88166_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88166_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88166_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88166_I2STXEN_START_BIT (9)
+#define AW88166_I2STXEN_BITS_LEN (1)
+#define AW88166_I2STXEN_MASK \
+ (~(((1<<AW88166_I2STXEN_BITS_LEN)-1) << AW88166_I2STXEN_START_BIT))
+
+#define AW88166_I2STXEN_DISABLE (0)
+#define AW88166_I2STXEN_DISABLE_VALUE \
+ (AW88166_I2STXEN_DISABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_I2STXEN_ENABLE (1)
+#define AW88166_I2STXEN_ENABLE_VALUE \
+ (AW88166_I2STXEN_ENABLE << AW88166_I2STXEN_START_BIT)
+
+#define AW88166_VOL_START_BIT (0)
+#define AW88166_VOL_BITS_LEN (10)
+#define AW88166_VOL_MASK \
+ (~(((1<<AW88166_VOL_BITS_LEN)-1) << AW88166_VOL_START_BIT))
+
+#define AW88166_PWDN_START_BIT (0)
+#define AW88166_PWDN_BITS_LEN (1)
+#define AW88166_PWDN_MASK \
+ (~(((1<<AW88166_PWDN_BITS_LEN)-1) << AW88166_PWDN_START_BIT))
+
+#define AW88166_PWDN_POWER_DOWN (1)
+#define AW88166_PWDN_POWER_DOWN_VALUE \
+ (AW88166_PWDN_POWER_DOWN << AW88166_PWDN_START_BIT)
+
+#define AW88166_PWDN_WORKING (0)
+#define AW88166_PWDN_WORKING_VALUE \
+ (AW88166_PWDN_WORKING << AW88166_PWDN_START_BIT)
+
+#define AW88166_DSPBY_START_BIT (2)
+#define AW88166_DSPBY_BITS_LEN (1)
+#define AW88166_DSPBY_MASK \
+ (~(((1<<AW88166_DSPBY_BITS_LEN)-1) << AW88166_DSPBY_START_BIT))
+
+#define AW88166_DSPBY_WORKING (0)
+#define AW88166_DSPBY_WORKING_VALUE \
+ (AW88166_DSPBY_WORKING << AW88166_DSPBY_START_BIT)
+
+#define AW88166_DSPBY_BYPASS (1)
+#define AW88166_DSPBY_BYPASS_VALUE \
+ (AW88166_DSPBY_BYPASS << AW88166_DSPBY_START_BIT)
+
+#define AW88166_MEM_CLKSEL_START_BIT (3)
+#define AW88166_MEM_CLKSEL_BITS_LEN (1)
+#define AW88166_MEM_CLKSEL_MASK \
+ (~(((1<<AW88166_MEM_CLKSEL_BITS_LEN)-1) << AW88166_MEM_CLKSEL_START_BIT))
+
+#define AW88166_MEM_CLKSEL_OSCCLK (0)
+#define AW88166_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88166_MEM_CLKSEL_OSCCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_MEM_CLKSEL_DAPHCLK (1)
+#define AW88166_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88166_MEM_CLKSEL_DAPHCLK << AW88166_MEM_CLKSEL_START_BIT)
+
+#define AW88166_DITHER_EN_START_BIT (15)
+#define AW88166_DITHER_EN_BITS_LEN (1)
+#define AW88166_DITHER_EN_MASK \
+ (~(((1<<AW88166_DITHER_EN_BITS_LEN)-1) << AW88166_DITHER_EN_START_BIT))
+
+#define AW88166_DITHER_EN_DISABLE (0)
+#define AW88166_DITHER_EN_DISABLE_VALUE \
+ (AW88166_DITHER_EN_DISABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_DITHER_EN_ENABLE (1)
+#define AW88166_DITHER_EN_ENABLE_VALUE \
+ (AW88166_DITHER_EN_ENABLE << AW88166_DITHER_EN_START_BIT)
+
+#define AW88166_HMUTE_START_BIT (8)
+#define AW88166_HMUTE_BITS_LEN (1)
+#define AW88166_HMUTE_MASK \
+ (~(((1<<AW88166_HMUTE_BITS_LEN)-1) << AW88166_HMUTE_START_BIT))
+
+#define AW88166_HMUTE_DISABLE (0)
+#define AW88166_HMUTE_DISABLE_VALUE \
+ (AW88166_HMUTE_DISABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_HMUTE_ENABLE (1)
+#define AW88166_HMUTE_ENABLE_VALUE \
+ (AW88166_HMUTE_ENABLE << AW88166_HMUTE_START_BIT)
+
+#define AW88166_EF_DBMD_START_BIT (2)
+#define AW88166_EF_DBMD_BITS_LEN (1)
+#define AW88166_EF_DBMD_MASK \
+ (~(((1<<AW88166_EF_DBMD_BITS_LEN)-1) << AW88166_EF_DBMD_START_BIT))
+
+#define AW88166_EF_DBMD_OR (1)
+#define AW88166_EF_DBMD_OR_VALUE \
+ (AW88166_EF_DBMD_OR << AW88166_EF_DBMD_START_BIT)
+
+#define AW88166_CLKI_START_BIT (4)
+#define AW88166_NOCLKI_START_BIT (5)
+#define AW88166_PLLI_START_BIT (0)
+#define AW88166_PLLI_INT_VALUE (1)
+#define AW88166_PLLI_INT_INTERRUPT \
+ (AW88166_PLLI_INT_VALUE << AW88166_PLLI_START_BIT)
+
+#define AW88166_CLKI_INT_VALUE (1)
+#define AW88166_CLKI_INT_INTERRUPT \
+ (AW88166_CLKI_INT_VALUE << AW88166_CLKI_START_BIT)
+
+#define AW88166_NOCLKI_INT_VALUE (1)
+#define AW88166_NOCLKI_INT_INTERRUPT \
+ (AW88166_NOCLKI_INT_VALUE << AW88166_NOCLKI_START_BIT)
+
+#define AW88166_BIT_SYSINT_CHECK \
+ (AW88166_PLLI_INT_INTERRUPT | \
+ AW88166_CLKI_INT_INTERRUPT | \
+ AW88166_NOCLKI_INT_INTERRUPT)
+
+#define AW88166_CRC_CHECK_START_BIT (12)
+#define AW88166_CRC_CHECK_BITS_LEN (3)
+#define AW88166_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88166_CRC_CHECK_BITS_LEN)-1) << AW88166_CRC_CHECK_START_BIT))
+
+#define AW88166_RCV_MODE_RECEIVER (1)
+#define AW88166_RCV_MODE_RECEIVER_VALUE \
+ (AW88166_RCV_MODE_RECEIVER << AW88166_RCV_MODE_START_BIT)
+
+#define AW88166_AMPPD_START_BIT (1)
+#define AW88166_AMPPD_BITS_LEN (1)
+#define AW88166_AMPPD_MASK \
+ (~(((1<<AW88166_AMPPD_BITS_LEN)-1) << AW88166_AMPPD_START_BIT))
+
+#define AW88166_AMPPD_WORKING (0)
+#define AW88166_AMPPD_WORKING_VALUE \
+ (AW88166_AMPPD_WORKING << AW88166_AMPPD_START_BIT)
+
+#define AW88166_AMPPD_POWER_DOWN (1)
+#define AW88166_AMPPD_POWER_DOWN_VALUE \
+ (AW88166_AMPPD_POWER_DOWN << AW88166_AMPPD_START_BIT)
+
+#define AW88166_RAM_CG_BYP_START_BIT (0)
+#define AW88166_RAM_CG_BYP_BITS_LEN (1)
+#define AW88166_RAM_CG_BYP_MASK \
+ (~(((1<<AW88166_RAM_CG_BYP_BITS_LEN)-1) << AW88166_RAM_CG_BYP_START_BIT))
+
+#define AW88166_RAM_CG_BYP_WORK (0)
+#define AW88166_RAM_CG_BYP_WORK_VALUE \
+ (AW88166_RAM_CG_BYP_WORK << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_RAM_CG_BYP_BYPASS (1)
+#define AW88166_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88166_RAM_CG_BYP_BYPASS << AW88166_RAM_CG_BYP_START_BIT)
+
+#define AW88166_CRC_END_ADDR_START_BIT (0)
+#define AW88166_CRC_END_ADDR_BITS_LEN (12)
+#define AW88166_CRC_END_ADDR_MASK \
+ (~(((1<<AW88166_CRC_END_ADDR_BITS_LEN)-1) << AW88166_CRC_END_ADDR_START_BIT))
+
+#define AW88166_CRC_CODE_EN_START_BIT (13)
+#define AW88166_CRC_CODE_EN_BITS_LEN (1)
+#define AW88166_CRC_CODE_EN_MASK \
+ (~(((1<<AW88166_CRC_CODE_EN_BITS_LEN)-1) << AW88166_CRC_CODE_EN_START_BIT))
+
+#define AW88166_CRC_CODE_EN_DISABLE (0)
+#define AW88166_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88166_CRC_CODE_EN_DISABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CODE_EN_ENABLE (1)
+#define AW88166_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88166_CRC_CODE_EN_ENABLE << AW88166_CRC_CODE_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_START_BIT (12)
+#define AW88166_CRC_CFG_EN_BITS_LEN (1)
+#define AW88166_CRC_CFG_EN_MASK \
+ (~(((1<<AW88166_CRC_CFG_EN_BITS_LEN)-1) << AW88166_CRC_CFG_EN_START_BIT))
+
+#define AW88166_CRC_CFG_EN_DISABLE (0)
+#define AW88166_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88166_CRC_CFG_EN_DISABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_CRC_CFG_EN_ENABLE (1)
+#define AW88166_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88166_CRC_CFG_EN_ENABLE << AW88166_CRC_CFG_EN_START_BIT)
+
+#define AW88166_OCDS_START_BIT (3)
+#define AW88166_OCDS_OC (1)
+#define AW88166_OCDS_OC_VALUE \
+ (AW88166_OCDS_OC << AW88166_OCDS_START_BIT)
+
+#define AW88166_NOCLKS_START_BIT (5)
+#define AW88166_NOCLKS_NO_CLOCK (1)
+#define AW88166_NOCLKS_NO_CLOCK_VALUE \
+ (AW88166_NOCLKS_NO_CLOCK << AW88166_NOCLKS_START_BIT)
+
+#define AW88166_SWS_START_BIT (8)
+#define AW88166_SWS_SWITCHING (1)
+#define AW88166_SWS_SWITCHING_VALUE \
+ (AW88166_SWS_SWITCHING << AW88166_SWS_START_BIT)
+
+#define AW88166_BSTS_START_BIT (9)
+#define AW88166_BSTS_FINISHED (1)
+#define AW88166_BSTS_FINISHED_VALUE \
+ (AW88166_BSTS_FINISHED << AW88166_BSTS_START_BIT)
+
+#define AW88166_UVLS_START_BIT (14)
+#define AW88166_UVLS_NORMAL (0)
+#define AW88166_UVLS_NORMAL_VALUE \
+ (AW88166_UVLS_NORMAL << AW88166_UVLS_START_BIT)
+
+#define AW88166_BSTOCS_START_BIT (11)
+#define AW88166_BSTOCS_OVER_CURRENT (1)
+#define AW88166_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88166_BSTOCS_OVER_CURRENT << AW88166_BSTOCS_START_BIT)
+
+#define AW88166_OTHS_START_BIT (1)
+#define AW88166_OTHS_OT (1)
+#define AW88166_OTHS_OT_VALUE \
+ (AW88166_OTHS_OT << AW88166_OTHS_START_BIT)
+
+#define AW88166_PLLS_START_BIT (0)
+#define AW88166_PLLS_LOCKED (1)
+#define AW88166_PLLS_LOCKED_VALUE \
+ (AW88166_PLLS_LOCKED << AW88166_PLLS_START_BIT)
+
+#define AW88166_CLKS_START_BIT (4)
+#define AW88166_CLKS_STABLE (1)
+#define AW88166_CLKS_STABLE_VALUE \
+ (AW88166_CLKS_STABLE << AW88166_CLKS_START_BIT)
+
+#define AW88166_BIT_PLL_CHECK \
+ (AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_CHECK_MASK \
+ (~(AW88166_UVLS_NORMAL_VALUE | \
+ AW88166_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE | \
+ AW88166_NOCLKS_NO_CLOCK_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_OCDS_OC_VALUE | \
+ AW88166_OTHS_OT_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE))
+
+#define AW88166_BIT_SYSST_NOSWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE)
+
+#define AW88166_BIT_SYSST_SWS_CHECK \
+ (AW88166_BSTS_FINISHED_VALUE | \
+ AW88166_CLKS_STABLE_VALUE | \
+ AW88166_PLLS_LOCKED_VALUE | \
+ AW88166_SWS_SWITCHING_VALUE)
+
+#define AW88166_CCO_MUX_START_BIT (14)
+#define AW88166_CCO_MUX_BITS_LEN (1)
+#define AW88166_CCO_MUX_MASK \
+ (~(((1<<AW88166_CCO_MUX_BITS_LEN)-1) << AW88166_CCO_MUX_START_BIT))
+
+#define AW88166_CCO_MUX_DIVIDED (0)
+#define AW88166_CCO_MUX_DIVIDED_VALUE \
+ (AW88166_CCO_MUX_DIVIDED << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_CCO_MUX_BYPASS (1)
+#define AW88166_CCO_MUX_BYPASS_VALUE \
+ (AW88166_CCO_MUX_BYPASS << AW88166_CCO_MUX_START_BIT)
+
+#define AW88166_NOISE_GATE_EN_START_BIT (13)
+#define AW88166_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88166_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88166_NOISE_GATE_EN_BITS_LEN)-1) << AW88166_NOISE_GATE_EN_START_BIT))
+
+#define AW88166_WDT_CNT_START_BIT (0)
+#define AW88166_WDT_CNT_BITS_LEN (8)
+#define AW88166_WDT_CNT_MASK \
+ (~(((1<<AW88166_WDT_CNT_BITS_LEN)-1) << AW88166_WDT_CNT_START_BIT))
+
+#define AW88166_EF_ISN_GESLP_START_BIT (0)
+#define AW88166_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88166_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88166_EF_ISN_GESLP_BITS_LEN)-1) << AW88166_EF_ISN_GESLP_START_BIT))
+#define AW88166_EF_ISN_GESLP_SHIFT (0)
+
+#define AW88166_EF_VSN_GESLP_START_BIT (10)
+#define AW88166_EF_VSN_GESLP_BITS_LEN (6)
+#define AW88166_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88166_EF_VSN_GESLP_BITS_LEN)-1) << AW88166_EF_VSN_GESLP_START_BIT))
+#define AW88166_EF_VSN_GESLP_SHIFT (10)
+
+#define AW88166_EF_VSN_H3BITS_START_BIT (13)
+#define AW88166_EF_VSN_H3BITS_BITS_LEN (3)
+#define AW88166_EF_VSN_H3BITS_MASK \
+ (~(((1<<AW88166_EF_VSN_H3BITS_BITS_LEN)-1) << AW88166_EF_VSN_H3BITS_START_BIT))
+#define AW88166_EF_VSN_H3BITS_SHIFT (10)
+#define AW88166_EF_VSN_H3BITS_SIGN_MASK (0x7)
+
+#define AW88166_EF_ISN_H5BITS_START_BIT (8)
+#define AW88166_EF_ISN_H5BITS_BITS_LEN (5)
+#define AW88166_EF_ISN_H5BITS_MASK \
+ (~(((1<<AW88166_EF_ISN_H5BITS_BITS_LEN)-1) << AW88166_EF_ISN_H5BITS_START_BIT))
+#define AW88166_EF_ISN_H5BITS_SIGN_MASK (0x1F)
+#define AW88166_EF_ISN_H5BITS_SHIFT (3)
+
+#define AW88166_VSCAL_FACTOR (65300)
+#define AW88166_ISCAL_FACTOR (34667)
+#define AW88166_CABL_BASE_VALUE (1000)
+#define AW88166_VCALK_SIGN_MASK (~(1 << 5))
+#define AW88166_VCALK_NEG_MASK (0xFFE0)
+#define AW88166_ICALK_SIGN_MASK (~(1 << 9))
+#define AW88166_ICALK_NEG_MASK (0xFE00)
+#define AW88166_ICABLK_FACTOR (1)
+#define AW88166_VCABLK_FACTOR (2)
+#define AW88166_VCALB_ADJ_FACTOR (12)
+#define AW88166_VCALB_ACCURACY (1 << 12)
+#define AW88166_DSP_RE_SHIFT (12)
+#define AW88166_CALI_RE_MAX (15000)
+#define AW88166_CALI_RE_MIN (4000)
+#define AW88166_VOLUME_STEP_DB (64)
+#define AW88166_VOL_DEFAULT_VALUE (0)
+#define AW88166_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88166_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+
+#define AW88166_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88166_DSP_ROM_CHECK_DATA (0xFF99)
+
+#define AW88166_DEV_DEFAULT_CH (0)
+#define AW88166_DEV_DSP_CHECK_MAX (5)
+#define AW88166_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW_FW_ADDR_LEN (4)
+#define AW88166_CRC_CHECK_PASS_VAL (0x4)
+#define AW88166_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88166_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88166_DEV_SYSST_CHECK_MAX (10)
+#define AW88166_START_RETRIES (5)
+#define AW88166_START_WORK_DELAY_MS (0)
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+#define AW88166_CHIP_ID (0x2066)
+#define AW88166_I2C_NAME "aw88166"
+#define AW88166_ACF_FILE "aw88166_acf.bin"
+
+#define AW88166_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88166_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define AW88166_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 {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88166_DSP_FW_UPDATE_OFF = 0,
+ AW88166_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_FORCE_UPDATE_OFF = 0,
+ AW88166_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88166_1000_US = 1000,
+ AW88166_2000_US = 2000,
+ AW88166_3000_US = 3000,
+ AW88166_4000_US = 4000,
+};
+
+enum AW88166_DEV_STATUS {
+ AW88166_DEV_PW_OFF = 0,
+ AW88166_DEV_PW_ON,
+};
+
+enum AW88166_DEV_FW_STATUS {
+ AW88166_DEV_FW_FAILED = 0,
+ AW88166_DEV_FW_OK,
+};
+
+enum AW88166_DEV_MEMCLK {
+ AW88166_DEV_MEMCLK_OSC = 0,
+ AW88166_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88166_DEV_DSP_CFG {
+ AW88166_DEV_DSP_WORK = 0,
+ AW88166_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88166_DSP_16_DATA = 0,
+ AW88166_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88166_SYNC_START = 0,
+ AW88166_ASYNC_START,
+};
+
+enum {
+ AW88166_RECORD_SEC_DATA = 0,
+ AW88166_RECOVERY_SEC_DATA = 1,
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
new file mode 100644
index 000000000000..de11ae8dd9d9
--- /dev/null
+++ b/sound/soc/codecs/aw88261.c
@@ -0,0 +1,1282 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.c -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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,
+ .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, &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, &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, &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, &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, &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, &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, &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, &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 int aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+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;
+
+ ret = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret) {
+ 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);
+ char *prof_name;
+ int count, ret;
+
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88261_dev_get_prof_name(aw88261->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_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, &reg_val);
+ if (ret)
+ return ret;
+ temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, &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_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;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw88261->phase_sync = of_property_read_bool(np, "awinic,sync-flag");
+
+ aw_dev->channel = channel_value;
+}
+
+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->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 },
+ { }
+};
+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 000000000000..734d0f93ced9
--- /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 <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#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_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_UVLS_START_BIT (14)
+#define AW88261_UVLS_NORMAL (0)
+#define AW88261_UVLS_NORMAL_VALUE \
+ (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT)
+
+#define AW88261_BSTOCS_START_BIT (11)
+#define AW88261_BSTOCS_OVER_CURRENT (1)
+#define AW88261_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT)
+
+#define AW88261_BSTS_START_BIT (9)
+#define AW88261_BSTS_FINISHED (1)
+#define AW88261_BSTS_FINISHED_VALUE \
+ (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT)
+
+#define AW88261_SWS_START_BIT (8)
+#define AW88261_SWS_SWITCHING (1)
+#define AW88261_SWS_SWITCHING_VALUE \
+ (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT)
+
+#define AW88261_NOCLKS_START_BIT (5)
+#define AW88261_NOCLKS_NO_CLOCK (1)
+#define AW88261_NOCLKS_NO_CLOCK_VALUE \
+ (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT)
+
+#define AW88261_CLKS_START_BIT (4)
+#define AW88261_CLKS_STABLE (1)
+#define AW88261_CLKS_STABLE_VALUE \
+ (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT)
+
+#define AW88261_OCDS_START_BIT (3)
+#define AW88261_OCDS_OC (1)
+#define AW88261_OCDS_OC_VALUE \
+ (AW88261_OCDS_OC << AW88261_OCDS_START_BIT)
+
+#define AW88261_OTHS_START_BIT (1)
+#define AW88261_OTHS_OT (1)
+#define AW88261_OTHS_OT_VALUE \
+ (AW88261_OTHS_OT << AW88261_OTHS_START_BIT)
+
+#define AW88261_PLLS_START_BIT (0)
+#define AW88261_PLLS_LOCKED (1)
+#define AW88261_PLLS_LOCKED_VALUE \
+ (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT)
+
+#define AW88261_BIT_PLL_CHECK \
+ (AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_BIT_SYSST_CHECK_MASK \
+ (~(AW88261_UVLS_NORMAL_VALUE | \
+ AW88261_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_NOCLKS_NO_CLOCK_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_OCDS_OC_VALUE | \
+ AW88261_OTHS_OT_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE))
+
+#define AW88261_BIT_SYSST_CHECK \
+ (AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_ULS_HMUTE_START_BIT (14)
+#define AW88261_ULS_HMUTE_BITS_LEN (1)
+#define AW88261_ULS_HMUTE_MASK \
+ (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT))
+
+#define AW88261_ULS_HMUTE_DISABLE (0)
+#define AW88261_ULS_HMUTE_DISABLE_VALUE \
+ (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_ULS_HMUTE_ENABLE (1)
+#define AW88261_ULS_HMUTE_ENABLE_VALUE \
+ (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_START_BIT (8)
+#define AW88261_HMUTE_BITS_LEN (1)
+#define AW88261_HMUTE_MASK \
+ (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT))
+
+#define AW88261_HMUTE_DISABLE (0)
+#define AW88261_HMUTE_DISABLE_VALUE \
+ (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_ENABLE (1)
+#define AW88261_HMUTE_ENABLE_VALUE \
+ (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_AMPPD_WORKING (0)
+#define AW88261_AMPPD_WORKING_VALUE \
+ (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT)
+
+#define AW88261_AMPPD_POWER_DOWN (1)
+#define AW88261_AMPPD_POWER_DOWN_VALUE \
+ (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT)
+
+#define AW88261_PWDN_START_BIT (0)
+#define AW88261_PWDN_BITS_LEN (1)
+#define AW88261_PWDN_MASK \
+ (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT))
+
+#define AW88261_PWDN_WORKING (0)
+#define AW88261_PWDN_WORKING_VALUE \
+ (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT)
+
+#define AW88261_PWDN_POWER_DOWN (1)
+#define AW88261_PWDN_POWER_DOWN_VALUE \
+ (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT)
+
+#define AW88261_MUTE_VOL (90 * 8)
+#define AW88261_VOLUME_STEP_DB (6 * 8)
+
+#define AW88261_VOL_6DB_START (6)
+
+#define AW88261_VOL_START_BIT (0)
+#define AW88261_VOL_BITS_LEN (10)
+#define AW88261_VOL_MASK \
+ (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
+
+#define AW88261_VOL_DEFAULT_VALUE (0)
+
+#define AW88261_I2STXEN_START_BIT (6)
+#define AW88261_I2STXEN_BITS_LEN (1)
+#define AW88261_I2STXEN_MASK \
+ (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT))
+
+#define AW88261_I2STXEN_DISABLE (0)
+#define AW88261_I2STXEN_DISABLE_VALUE \
+ (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_I2STXEN_ENABLE (1)
+#define AW88261_I2STXEN_ENABLE_VALUE \
+ (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT (14)
+#define AW88261_CCO_MUX_BITS_LEN (1)
+#define AW88261_CCO_MUX_MASK \
+ (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
+
+#define AW88261_CCO_MUX_DIVIDED (0)
+#define AW88261_CCO_MUX_DIVIDED_VALUE \
+ (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_CCO_MUX_BYPASS (1)
+#define AW88261_CCO_MUX_BYPASS_VALUE \
+ (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88261_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88261_FORCE_PWM_START_BIT (12)
+#define AW88261_FORCE_PWM_BITS_LEN (1)
+#define AW88261_FORCE_PWM_MASK \
+ (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT))
+
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1)
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \
+ (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT)
+
+#define AW88261_BST_OS_WIDTH_START_BIT (0)
+#define AW88261_BST_OS_WIDTH_BITS_LEN (3)
+#define AW88261_BST_OS_WIDTH_MASK \
+ (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT))
+
+#define AW88261_BST_OS_WIDTH_50NS (4)
+#define AW88261_BST_OS_WIDTH_50NS_VALUE \
+ (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT)
+
+/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */
+#define AW88261_BST_LOOPR_START_BIT (0)
+#define AW88261_BST_LOOPR_BITS_LEN (2)
+#define AW88261_BST_LOOPR_MASK \
+ (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT))
+
+#define AW88261_BST_LOOPR_340K (2)
+#define AW88261_BST_LOOPR_340K_VALUE \
+ (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT)
+
+/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */
+#define AW88261_RSQN_DLY_START_BIT (14)
+#define AW88261_RSQN_DLY_BITS_LEN (2)
+#define AW88261_RSQN_DLY_MASK \
+ (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT))
+
+#define AW88261_RSQN_DLY_35NS (2)
+#define AW88261_RSQN_DLY_35NS_VALUE \
+ (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT)
+
+/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */
+#define AW88261_BURST_SSMODE_START_BIT (3)
+#define AW88261_BURST_SSMODE_BITS_LEN (1)
+#define AW88261_BURST_SSMODE_MASK \
+ (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT))
+
+#define AW88261_BURST_SSMODE_FAST (0)
+#define AW88261_BURST_SSMODE_FAST_VALUE \
+ (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT)
+
+/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */
+#define AW88261_BST_BURST_START_BIT (7)
+#define AW88261_BST_BURST_BITS_LEN (3)
+#define AW88261_BST_BURST_MASK \
+ (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT))
+
+#define AW88261_BST_BURST_30MA (2)
+#define AW88261_BST_BURST_30MA_VALUE \
+ (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_VSN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_ISN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT))
+
+#define AW88261_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT))
+
+#define AW88261_CABL_BASE_VALUE (1000)
+#define AW88261_ICABLK_FACTOR (1)
+#define AW88261_VCABLK_FACTOR (1)
+
+#define AW88261_VCAL_FACTOR (1<<13)
+
+#define AW88261_START_RETRIES (5)
+#define AW88261_START_WORK_DELAY_MS (0)
+
+#define AW88261_I2C_NAME "aw88261"
+
+#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88261_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 AW88261_DEV_DEFAULT_CH (0)
+#define AW88261_ACF_FILE "aw88261_acf.bin"
+#define AW88261_DEV_SYSST_CHECK_MAX (10)
+#define AW88261_SOFT_RESET_VALUE (0x55aa)
+#define AW88261_REG_TO_DB (0x3f)
+#define AW88261_VOL_START_MASK (0xfc00)
+#define AW88261_INIT_PROFILE (0)
+
+#define REG_VAL_TO_DB(value) ((((value) >> 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;
+
+ bool phase_sync;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
new file mode 100644
index 000000000000..fb563b4c6971
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -0,0 +1,576 @@
+// 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 <zhaolei@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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);
+ char *prof_name;
+ int count, ret;
+
+ 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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88395_dev_get_prof_name(aw88395->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_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, AW88395_CALI_RE_MAX, 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 },
+ { }
+};
+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 000000000000..c2a4f0cb8cd5
--- /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 <zhaolei@awinic.com>
+//
+
+#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"
+
+#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 000000000000..e7aa56178b36
--- /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 <zhaolei@awinic.com>
+//
+
+#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 000000000000..e1430940015d
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -0,0 +1,1720 @@
+// 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 <zhaolei@awinic.com>
+// Author: Ben Yi <yijiangtao@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/i2c.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#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, &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, &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, &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, &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 (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 (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(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, &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, &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, &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, &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, "unsupported 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, &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, &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, &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, &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, &reg_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, &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, &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, &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);
+ }
+
+ 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) {
+ tmp_len = min(len - i, 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, &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, &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, &reg_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, &reg_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;
+ }
+
+ ret = aw88395_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ 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, "awinic,audio-channel", &channel_value);
+ if (ret) {
+ dev_dbg(aw_dev->dev,
+ "read audio-channel failed,use default 0");
+ aw_dev->channel = AW88395_DEV_DEFAULT_CH;
+ return;
+ }
+
+ dev_dbg(aw_dev->dev, "read audio-channel value is: %d",
+ channel_value);
+ aw_dev->channel = channel_value;
+}
+
+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);
+
+ 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);
+
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+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 000000000000..6f8b30b475da
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -0,0 +1,214 @@
+// 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 <zhaolei@awinic.com>
+//
+
+#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;
+};
+
+enum {
+ CALI_RESULT_NORMAL,
+ CALI_RESULT_ERROR,
+};
+
+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;
+};
+
+#define AW_CALI_CFG_NUM (4)
+struct cali_cfg {
+ uint32_t data[AW_CALI_CFG_NUM];
+};
+
+struct aw_cali_backup_desc {
+ unsigned int dsp_ng_cfg;
+ unsigned int dsp_lp_cfg;
+};
+
+struct aw_cali_desc {
+ u32 cali_re;
+ u32 ra;
+ bool cali_switch;
+ bool cali_running;
+ uint16_t cali_result;
+ uint16_t store_vol;
+ struct cali_cfg cali_cfg;
+ struct aw_cali_backup_desc backup_info;
+};
+
+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;
+ unsigned char dsp_cfg;
+ u16 chip_id;
+
+ unsigned int channel;
+ unsigned int fade_step;
+ unsigned int prof_data_type;
+
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ char *acf;
+
+ 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);
+int aw88395_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name);
+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 000000000000..ceb7fc43d018
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -0,0 +1,1174 @@
+// 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 <zhaolei@awinic.com>
+//
+
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/i2c.h>
+#include "aw88395_lib.h"
+#include "aw88395_device.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)
+{
+ int ret;
+ int i;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(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");
+ return ret;
+ }
+
+ 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)
+ return -EINVAL;
+
+ 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)
+ return -EINVAL;
+
+ 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;
+
+ return 0;
+}
+
+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)
+{
+ int ret;
+
+ struct aw_bin *aw_bin __free(kfree) = kzalloc(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");
+ return ret;
+ }
+
+ 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");
+ return -EINVAL;
+ }
+
+ 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;
+
+ return 0;
+}
+
+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;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ 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;
+ }
+ aw_dev->prof_data_type = cfg_dde[i].data_type;
+ 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 aw_dev_cfg_get_reg_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 aw_dev_cfg_get_multiple_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)
+{
+ int ret;
+
+ struct aw_all_prof_info *all_prof_info __free(kfree) = kzalloc(sizeof(*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) {
+ return ret;
+ } 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)
+ return ret;
+ }
+
+ switch (aw_dev->prof_data_type) {
+ case ACF_SEC_TYPE_MULTIPLE_BIN:
+ ret = aw_dev_cfg_get_multiple_valid_prof(aw_dev, all_prof_info);
+ break;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_cfg_get_reg_valid_prof(aw_dev, all_prof_info);
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported data type\n");
+ ret = -EINVAL;
+ break;
+ }
+ if (!ret)
+ aw_dev->prof_info.prof_name_list = profile_name;
+
+ 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;
+
+ 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) ||
+ (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)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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;
+
+
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) ||
+ (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)++;
+ }
+
+ if ((*scene_num) == 0) {
+ dev_err(aw_dev->dev, "failed to obtain scene, scenu_num = %d\n", (*scene_num));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+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 000000000000..8a620920d8bd
--- /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 <zhaolei@awinic.com>
+//
+
+#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 000000000000..e64f24e97150
--- /dev/null
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -0,0 +1,383 @@
+// 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 <zhaolei@awinic.com>
+//
+
+#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,
+};
+
+#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<<AW88395_DSPS_BITS_LEN)-1) << AW88395_DSPS_START_BIT))
+
+#define AW88395_DSPS_NORMAL (0)
+#define AW88395_DSPS_NORMAL_VALUE \
+ (AW88395_DSPS_NORMAL << AW88395_DSPS_START_BIT)
+
+#define AW88395_BSTOCS_START_BIT (11)
+#define AW88395_BSTOCS_OVER_CURRENT (1)
+#define AW88395_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88395_BSTOCS_OVER_CURRENT << AW88395_BSTOCS_START_BIT)
+
+#define AW88395_BSTS_START_BIT (9)
+#define AW88395_BSTS_FINISHED (1)
+#define AW88395_BSTS_FINISHED_VALUE \
+ (AW88395_BSTS_FINISHED << AW88395_BSTS_START_BIT)
+
+#define AW88395_SWS_START_BIT (8)
+#define AW88395_SWS_SWITCHING (1)
+#define AW88395_SWS_SWITCHING_VALUE \
+ (AW88395_SWS_SWITCHING << AW88395_SWS_START_BIT)
+
+#define AW88395_NOCLKS_START_BIT (5)
+#define AW88395_NOCLKS_NO_CLOCK (1)
+#define AW88395_NOCLKS_NO_CLOCK_VALUE \
+ (AW88395_NOCLKS_NO_CLOCK << AW88395_NOCLKS_START_BIT)
+
+#define AW88395_CLKS_START_BIT (4)
+#define AW88395_CLKS_STABLE (1)
+#define AW88395_CLKS_STABLE_VALUE \
+ (AW88395_CLKS_STABLE << AW88395_CLKS_START_BIT)
+
+#define AW88395_OCDS_START_BIT (3)
+#define AW88395_OCDS_OC (1)
+#define AW88395_OCDS_OC_VALUE \
+ (AW88395_OCDS_OC << AW88395_OCDS_START_BIT)
+
+#define AW88395_OTHS_START_BIT (1)
+#define AW88395_OTHS_OT (1)
+#define AW88395_OTHS_OT_VALUE \
+ (AW88395_OTHS_OT << AW88395_OTHS_START_BIT)
+
+#define AW88395_PLLS_START_BIT (0)
+#define AW88395_PLLS_LOCKED (1)
+#define AW88395_PLLS_LOCKED_VALUE \
+ (AW88395_PLLS_LOCKED << AW88395_PLLS_START_BIT)
+
+#define AW88395_BIT_PLL_CHECK \
+ (AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_BIT_SYSST_CHECK_MASK \
+ (~(AW88395_UVLS_NORMAL_VALUE | \
+ AW88395_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_NOCLKS_NO_CLOCK_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_OCDS_OC_VALUE | \
+ AW88395_OTHS_OT_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE))
+
+#define AW88395_BIT_SYSST_CHECK \
+ (AW88395_BSTS_FINISHED_VALUE | \
+ AW88395_SWS_SWITCHING_VALUE | \
+ AW88395_CLKS_STABLE_VALUE | \
+ AW88395_PLLS_LOCKED_VALUE)
+
+#define AW88395_WDI_START_BIT (6)
+#define AW88395_WDI_INT_VALUE (1)
+#define AW88395_WDI_INTERRUPT \
+ (AW88395_WDI_INT_VALUE << AW88395_WDI_START_BIT)
+
+#define AW88395_NOCLKI_START_BIT (5)
+#define AW88395_NOCLKI_INT_VALUE (1)
+#define AW88395_NOCLKI_INTERRUPT \
+ (AW88395_NOCLKI_INT_VALUE << AW88395_NOCLKI_START_BIT)
+
+#define AW88395_CLKI_START_BIT (4)
+#define AW88395_CLKI_INT_VALUE (1)
+#define AW88395_CLKI_INTERRUPT \
+ (AW88395_CLKI_INT_VALUE << AW88395_CLKI_START_BIT)
+
+#define AW88395_PLLI_START_BIT (0)
+#define AW88395_PLLI_INT_VALUE (1)
+#define AW88395_PLLI_INTERRUPT \
+ (AW88395_PLLI_INT_VALUE << AW88395_PLLI_START_BIT)
+
+#define AW88395_BIT_SYSINT_CHECK \
+ (AW88395_WDI_INTERRUPT | \
+ AW88395_CLKI_INTERRUPT | \
+ AW88395_NOCLKI_INTERRUPT | \
+ AW88395_PLLI_INTERRUPT)
+
+#define AW88395_HMUTE_START_BIT (8)
+#define AW88395_HMUTE_BITS_LEN (1)
+#define AW88395_HMUTE_MASK \
+ (~(((1<<AW88395_HMUTE_BITS_LEN)-1) << AW88395_HMUTE_START_BIT))
+
+#define AW88395_HMUTE_DISABLE (0)
+#define AW88395_HMUTE_DISABLE_VALUE \
+ (AW88395_HMUTE_DISABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_HMUTE_ENABLE (1)
+#define AW88395_HMUTE_ENABLE_VALUE \
+ (AW88395_HMUTE_ENABLE << AW88395_HMUTE_START_BIT)
+
+#define AW88395_RCV_MODE_START_BIT (7)
+#define AW88395_RCV_MODE_BITS_LEN (1)
+#define AW88395_RCV_MODE_MASK \
+ (~(((1<<AW88395_RCV_MODE_BITS_LEN)-1) << AW88395_RCV_MODE_START_BIT))
+
+#define AW88395_RCV_MODE_RECEIVER (1)
+#define AW88395_RCV_MODE_RECEIVER_VALUE \
+ (AW88395_RCV_MODE_RECEIVER << AW88395_RCV_MODE_START_BIT)
+
+#define AW88395_DSPBY_START_BIT (2)
+#define AW88395_DSPBY_BITS_LEN (1)
+#define AW88395_DSPBY_MASK \
+ (~(((1<<AW88395_DSPBY_BITS_LEN)-1) << AW88395_DSPBY_START_BIT))
+
+#define AW88395_DSPBY_WORKING (0)
+#define AW88395_DSPBY_WORKING_VALUE \
+ (AW88395_DSPBY_WORKING << AW88395_DSPBY_START_BIT)
+
+#define AW88395_DSPBY_BYPASS (1)
+#define AW88395_DSPBY_BYPASS_VALUE \
+ (AW88395_DSPBY_BYPASS << AW88395_DSPBY_START_BIT)
+
+#define AW88395_AMPPD_START_BIT (1)
+#define AW88395_AMPPD_BITS_LEN (1)
+#define AW88395_AMPPD_MASK \
+ (~(((1<<AW88395_AMPPD_BITS_LEN)-1) << AW88395_AMPPD_START_BIT))
+
+#define AW88395_AMPPD_WORKING (0)
+#define AW88395_AMPPD_WORKING_VALUE \
+ (AW88395_AMPPD_WORKING << AW88395_AMPPD_START_BIT)
+
+#define AW88395_AMPPD_POWER_DOWN (1)
+#define AW88395_AMPPD_POWER_DOWN_VALUE \
+ (AW88395_AMPPD_POWER_DOWN << AW88395_AMPPD_START_BIT)
+
+#define AW88395_PWDN_START_BIT (0)
+#define AW88395_PWDN_BITS_LEN (1)
+#define AW88395_PWDN_MASK \
+ (~(((1<<AW88395_PWDN_BITS_LEN)-1) << AW88395_PWDN_START_BIT))
+
+#define AW88395_PWDN_WORKING (0)
+#define AW88395_PWDN_WORKING_VALUE \
+ (AW88395_PWDN_WORKING << AW88395_PWDN_START_BIT)
+
+#define AW88395_PWDN_POWER_DOWN (1)
+#define AW88395_PWDN_POWER_DOWN_VALUE \
+ (AW88395_PWDN_POWER_DOWN << AW88395_PWDN_START_BIT)
+
+#define AW88395_MUTE_VOL (90 * 8)
+#define AW88395_VOLUME_STEP_DB (6 * 8)
+
+#define AW88395_VOL_6DB_START (6)
+#define AW88395_VOL_START_BIT (6)
+#define AW88395_VOL_BITS_LEN (10)
+#define AW88395_VOL_MASK \
+ (~(((1<<AW88395_VOL_BITS_LEN)-1) << AW88395_VOL_START_BIT))
+
+#define AW88395_VOL_DEFAULT_VALUE (0)
+
+#define AW88395_I2STXEN_START_BIT (0)
+#define AW88395_I2STXEN_BITS_LEN (1)
+#define AW88395_I2STXEN_MASK \
+ (~(((1<<AW88395_I2STXEN_BITS_LEN)-1) << AW88395_I2STXEN_START_BIT))
+
+#define AW88395_I2STXEN_DISABLE (0)
+#define AW88395_I2STXEN_DISABLE_VALUE \
+ (AW88395_I2STXEN_DISABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_I2STXEN_ENABLE (1)
+#define AW88395_I2STXEN_ENABLE_VALUE \
+ (AW88395_I2STXEN_ENABLE << AW88395_I2STXEN_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_START_BIT (15)
+#define AW88395_AGC_DSP_CTL_BITS_LEN (1)
+#define AW88395_AGC_DSP_CTL_MASK \
+ (~(((1<<AW88395_AGC_DSP_CTL_BITS_LEN)-1) << AW88395_AGC_DSP_CTL_START_BIT))
+
+#define AW88395_AGC_DSP_CTL_DISABLE (0)
+#define AW88395_AGC_DSP_CTL_DISABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_DISABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_AGC_DSP_CTL_ENABLE (1)
+#define AW88395_AGC_DSP_CTL_ENABLE_VALUE \
+ (AW88395_AGC_DSP_CTL_ENABLE << AW88395_AGC_DSP_CTL_START_BIT)
+
+#define AW88395_VDSEL_START_BIT (0)
+#define AW88395_VDSEL_BITS_LEN (1)
+#define AW88395_VDSEL_MASK \
+ (~(((1<<AW88395_VDSEL_BITS_LEN)-1) << AW88395_VDSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_START_BIT (3)
+#define AW88395_MEM_CLKSEL_BITS_LEN (1)
+#define AW88395_MEM_CLKSEL_MASK \
+ (~(((1<<AW88395_MEM_CLKSEL_BITS_LEN)-1) << AW88395_MEM_CLKSEL_START_BIT))
+
+#define AW88395_MEM_CLKSEL_OSC_CLK (0)
+#define AW88395_MEM_CLKSEL_OSC_CLK_VALUE \
+ (AW88395_MEM_CLKSEL_OSC_CLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_MEM_CLKSEL_DAP_HCLK (1)
+#define AW88395_MEM_CLKSEL_DAP_HCLK_VALUE \
+ (AW88395_MEM_CLKSEL_DAP_HCLK << AW88395_MEM_CLKSEL_START_BIT)
+
+#define AW88395_CCO_MUX_START_BIT (14)
+#define AW88395_CCO_MUX_BITS_LEN (1)
+#define AW88395_CCO_MUX_MASK \
+ (~(((1<<AW88395_CCO_MUX_BITS_LEN)-1) << AW88395_CCO_MUX_START_BIT))
+
+#define AW88395_CCO_MUX_DIVIDED (0)
+#define AW88395_CCO_MUX_DIVIDED_VALUE \
+ (AW88395_CCO_MUX_DIVIDED << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_CCO_MUX_BYPASS (1)
+#define AW88395_CCO_MUX_BYPASS_VALUE \
+ (AW88395_CCO_MUX_BYPASS << AW88395_CCO_MUX_START_BIT)
+
+#define AW88395_EF_VSN_GESLP_START_BIT (0)
+#define AW88395_EF_VSN_GESLP_BITS_LEN (10)
+#define AW88395_EF_VSN_GESLP_MASK \
+ (~(((1<<AW88395_EF_VSN_GESLP_BITS_LEN)-1) << AW88395_EF_VSN_GESLP_START_BIT))
+
+#define AW88395_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_EF_ISN_GESLP_START_BIT (0)
+#define AW88395_EF_ISN_GESLP_BITS_LEN (10)
+#define AW88395_EF_ISN_GESLP_MASK \
+ (~(((1<<AW88395_EF_ISN_GESLP_BITS_LEN)-1) << AW88395_EF_ISN_GESLP_START_BIT))
+
+#define AW88395_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88395_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88395_CABL_BASE_VALUE (1000)
+#define AW88395_ICABLK_FACTOR (1)
+#define AW88395_VCABLK_FACTOR (1)
+#define AW88395_VCAL_FACTOR (1 << 12)
+#define AW88395_VSCAL_FACTOR (16500)
+#define AW88395_ISCAL_FACTOR (3667)
+#define AW88395_EF_VSENSE_GAIN_SHIFT (0)
+
+#define AW88395_VCABLK_FACTOR_DAC (2)
+#define AW88395_VSCAL_FACTOR_DAC (11790)
+#define AW88395_EF_DAC_GESLP_SHIFT (10)
+#define AW88395_EF_DAC_GESLP_SIGN_MASK (1 << 5)
+#define AW88395_EF_DAC_GESLP_SIGN_NEG (0xffc0)
+
+#define AW88395_VCALB_ADJ_FACTOR (12)
+
+#define AW88395_WDT_CNT_START_BIT (0)
+#define AW88395_WDT_CNT_BITS_LEN (8)
+#define AW88395_WDT_CNT_MASK \
+ (~(((1<<AW88395_WDT_CNT_BITS_LEN)-1) << AW88395_WDT_CNT_START_BIT))
+
+#define AW88395_DSP_CFG_ADDR (0x9C80)
+#define AW88395_DSP_FW_ADDR (0x8C00)
+#define AW88395_DSP_REG_VMAX (0x9C94)
+#define AW88395_DSP_REG_CFG_ADPZ_RE (0x9D00)
+#define AW88395_DSP_REG_VCALB (0x9CF7)
+#define AW88395_DSP_RE_SHIFT (12)
+
+#define AW88395_DSP_REG_CFG_ADPZ_RA (0x9D02)
+#define AW88395_DSP_REG_CRC_ADDR (0x9F42)
+#define AW88395_DSP_CALI_F0_DELAY (0x9CFD)
+
+#endif
diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c
new file mode 100644
index 000000000000..58846feb013d
--- /dev/null
+++ b/sound/soc/codecs/aw88399.c
@@ -0,0 +1,2350 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.c -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/crc32.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/minmax.h>
+#include <linux/regmap.h>
+#include <linux/sort.h>
+#include <sound/soc.h>
+#include "aw88399.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88399_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88399_REG_MAX,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+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, AW88399_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, AW88399_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)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_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, AW88399_DSPMDAT_REG, 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, AW88399_DSPMDAT_REG, temp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+
+ return ret;
+}
+
+static int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ unsigned int 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",
+ 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",
+ 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, 0x00, &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, AW88399_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, AW88399_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, AW88399_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, AW88399_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, AW88399_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 AW88399_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 AW88399_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, AW88399_ID_REG, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. ret=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ 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, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_PWDN_MASK, AW88399_PWDN_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88399_SYSINT_REG, &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_dbg(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, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88399_BIT_PLL_CHECK) != AW88399_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 < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_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, AW88399_PLLCTRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88399_CCO_MUX_MASK);
+ if (reg_val == AW88399_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, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ regmap_update_bits(aw_dev->regmap, AW88399_PLLCTRL2_REG,
+ ~AW88399_CCO_MUX_MASK, AW88399_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88399_2000_US, AW88399_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 0;
+}
+
+static int aw_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_PWMCTRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if (reg_val & (~AW88399_NOISE_GATE_EN_MASK))
+ check_val = AW88399_BIT_SYSST_NOSWS_CHECK;
+ else
+ check_val = AW88399_BIT_SYSST_SWS_CHECK;
+
+ for (i = 0; i < AW88399_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ if ((reg_val & (~AW88399_BIT_SYSST_CHECK_MASK) & check_val) != check_val) {
+ dev_err(aw_dev->dev, "check sysst fail, cnt=%d, reg_val=0x%04x, check:0x%x",
+ i, reg_val, AW88399_BIT_SYSST_NOSWS_CHECK);
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ int ret;
+
+ if (amppd)
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_POWER_DOWN_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_AMPPD_MASK, AW88399_AMPPD_WORKING_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+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, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_WORKING_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_DSPBY_MASK, AW88399_DSPBY_BYPASS_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed\n", __func__);
+}
+
+static int aw88399_dev_get_icalk(struct aw88399 *aw88399, int16_t *icalk)
+{
+ uint16_t icalkh_val, icalkl_val, icalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkh_val = reg_val & (~AW88399_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+ icalkl_val = reg_val & (~AW88399_EF_ISN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ icalk_val = icalkh_val & icalkl_val;
+ else
+ icalk_val = icalkh_val | icalkl_val;
+
+ if (icalk_val & (~AW88399_EF_ISN_GESLP_SIGN_MASK))
+ icalk_val = icalk_val | AW88399_EF_ISN_GESLP_SIGN_NEG;
+ *icalk = (int16_t)icalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkh_val = reg_val & (~AW88399_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ vcalkl_val = reg_val & (~AW88399_EF_VSN_GESLP_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = vcalkh_val & vcalkl_val;
+ else
+ vcalk_val = vcalkh_val | vcalkl_val;
+
+ if (vcalk_val & AW88399_EF_VSN_GESLP_SIGN_MASK)
+ vcalk_val = vcalk_val | AW88399_EF_VSN_GESLP_SIGN_NEG;
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw88399_dev_get_internal_vcalk(struct aw88399 *aw88399, int16_t *vcalk)
+{
+ uint16_t vcalkh_val, vcalkl_val, vcalk_val;
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRH2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkh_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_EFRL2_REG, &reg_val);
+ if (ret)
+ return ret;
+ vcalkl_val = reg_val & (~AW88399_INTERNAL_VSN_TRIM_L_MASK);
+
+ if (aw88399->check_val == AW_EF_AND_CHECK)
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) &
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+ else
+ vcalk_val = (vcalkh_val >> AW88399_INTERNAL_VSN_TRIM_H_START_BIT) |
+ (vcalkl_val >> AW88399_INTERNAL_VSN_TRIM_L_START_BIT);
+
+ if (vcalk_val & (~AW88399_TEM4_SIGN_MASK))
+ vcalk_val = vcalk_val | AW88399_TEM4_SIGN_NEG;
+
+ *vcalk = (int16_t)vcalk_val;
+
+ return 0;
+}
+
+static int aw_dev_set_vcalb(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ unsigned int vsense_select, vsense_value;
+ int32_t ical_k, vcal_k, vcalb;
+ int16_t icalk, vcalk;
+ uint16_t reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_VSNCTRL1_REG, &vsense_value);
+ if (ret)
+ return ret;
+
+ vsense_select = vsense_value & (~AW88399_VDSEL_MASK);
+
+ ret = aw88399_dev_get_icalk(aw88399, &icalk);
+ if (ret) {
+ dev_err(aw_dev->dev, "get icalk failed\n");
+ return ret;
+ }
+
+ ical_k = icalk * AW88399_ICABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+
+ switch (vsense_select) {
+ case AW88399_DEV_VDSEL_VSENSE:
+ ret = aw88399_dev_get_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR *
+ ical_k / vcal_k * aw88399->vcalb_init_val;
+ break;
+ case AW88399_DEV_VDSEL_DAC:
+ ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk);
+ vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE;
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR /
+ AW88399_ISCAL_DAC_FACTOR * ical_k /
+ vcal_k * aw88399->vcalb_init_val;
+ break;
+ default:
+ dev_err(aw_dev->dev, "%s: unsupported vsense\n", __func__);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ vcalb = vcalb >> AW88399_VCALB_ADJ_FACTOR;
+ reg_val = (uint32_t)vcalb;
+
+ regmap_write(aw_dev->regmap, AW88399_DSPVCALB_REG, reg_val);
+
+ return 0;
+}
+
+static int 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);
+ uint16_t re_lbits, re_hbits;
+ u32 cali_re;
+ int ret;
+
+ if ((aw_dev->cali_desc.cali_re >= AW88399_CALI_RE_MAX) ||
+ (aw_dev->cali_desc.cali_re <= AW88399_CALI_RE_MIN))
+ return -EINVAL;
+
+ cali_re = AW88399_SHOW_RE_TO_DSP_RE((aw_dev->cali_desc.cali_re +
+ aw_dev->cali_desc.ra), AW88399_DSP_RE_SHIFT);
+
+ re_hbits = (cali_re & (~AW88399_CALI_RE_HBITS_MASK)) >> AW88399_CALI_RE_HBITS_SHIFT;
+ re_lbits = (cali_re & (~AW88399_CALI_RE_LBITS_MASK)) >> AW88399_CALI_RE_LBITS_SHIFT;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR1_REG, re_hbits);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali re error");
+ return ret;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_ACR2_REG, re_lbits);
+ if (ret)
+ dev_err(aw_dev->dev, "set cali re error");
+
+ return ret;
+}
+
+static int aw_dev_fw_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, fw_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate fw_end_addr */
+ fw_len_val = ((aw_dev->dsp_fw_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_FW_BASE_ADDR;
+
+ /* write fw_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, fw_len_val);
+ if (ret)
+ return ret;
+ /* enable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_ENABLE_VALUE);
+
+ usleep_range(AW88399_2000_US, AW88399_2000_US + 10);
+
+ /* read crc check result */
+ regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable fw crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CODE_EN_MASK, AW88399_CRC_CODE_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "%s failed, check_val 0x%x != 0x%x",
+ __func__, check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_cfg_crc_check(struct aw_device *aw_dev)
+{
+ uint16_t check_val, cfg_len_val;
+ unsigned int reg_val;
+ int ret;
+
+ /* calculate cfg end addr */
+ cfg_len_val = ((aw_dev->dsp_cfg_len / AW_FW_ADDR_LEN) - 1) + AW88399_CRC_CFG_BASE_ADDR;
+
+ /* write cfg_end_addr to crc_end_addr */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_END_ADDR_MASK, cfg_len_val);
+ if (ret)
+ return ret;
+
+ /* enable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_ENABLE_VALUE);
+ if (ret)
+ return ret;
+
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ /* read crc check result */
+ ret = regmap_read(aw_dev->regmap, AW88399_HAGCST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = (reg_val & (~AW88399_CRC_CHECK_BITS_MASK)) >> AW88399_CRC_CHECK_START_BIT;
+
+ /* disable cfg crc check */
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_CRCCTRL_REG,
+ ~AW88399_CRC_CFG_EN_MASK, AW88399_CRC_CFG_EN_DISABLE_VALUE);
+ if (ret)
+ return ret;
+
+ if (check_val != AW88399_CRC_CHECK_PASS_VAL) {
+ dev_err(aw_dev->dev, "crc_check failed, check val 0x%x != 0x%x",
+ check_val, AW88399_CRC_CHECK_PASS_VAL);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw_dev_hw_crc_check(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_BYPASS_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_fw_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = aw_dev_cfg_crc_check(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "cfg_crc_check failed\n");
+ goto crc_check_failed;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88399_CRCCTRL_REG, aw88399->crc_init_val);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+
+ return ret;
+
+crc_check_failed:
+ regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_RAM_CG_BYP_MASK, AW88399_RAM_CG_BYP_WORK_VALUE);
+ 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, AW88399_I2SCTRL3_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_ENABLE_VALUE);
+ else
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_I2SCFG1_REG,
+ ~AW88399_I2STXEN_MASK, AW88399_I2STXEN_DISABLE_VALUE);
+
+ if (ret)
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+}
+
+static int aw_dev_get_dsp_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88399_WDT_REG, &reg_val);
+ if (ret)
+ return ret;
+ if (!(reg_val & (~AW88399_WDT_CNT_MASK)))
+ return -EPERM;
+
+ return 0;
+}
+
+static int aw_dev_dsp_check(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ switch (aw_dev->dsp_cfg) {
+ case AW88399_DEV_DSP_BYPASS:
+ dev_dbg(aw_dev->dev, "dsp bypass");
+ ret = 0;
+ break;
+ case AW88399_DEV_DSP_WORK:
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_dsp_enable(aw_dev, true);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ for (i = 0; i < AW88399_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(AW88399_2000_US, AW88399_2000_US + 10);
+ }
+ }
+ break;
+ default:
+ dev_err(aw_dev->dev, "unknown dsp cfg=%d", aw_dev->dsp_cfg);
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+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;
+ int ret;
+
+ real_value = min((value + vol_desc->init_volume), (unsigned int)AW88399_MUTE_VOL);
+
+ ret = regmap_read(aw_dev->regmap, AW88399_SYSCTRL2_REG, &reg_value);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "value 0x%x , reg:0x%x", value, real_value);
+
+ real_value = (real_value << AW88399_VOL_START_BIT) | (reg_value & AW88399_VOL_MASK);
+
+ ret = regmap_write(aw_dev->regmap, AW88399_SYSCTRL2_REG, real_value);
+
+ return ret;
+}
+
+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 (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88399_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 (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88399_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 != AW88399_MUTE_VOL) {
+ aw_dev_set_volume(aw_dev, AW88399_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88399_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88399_SYSCTRL_REG,
+ ~AW88399_HMUTE_MASK, AW88399_HMUTE_DISABLE_VALUE);
+ aw_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88399_dev_set_dither(struct aw88399 *aw88399, bool dither)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (dither)
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_DITHER_EN_MASK, AW88399_DITHER_EN_DISABLE_VALUE);
+}
+
+static int aw88399_dev_start(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88399_DEV_PW_ON) {
+ dev_dbg(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ aw88399_dev_set_dither(aw88399, false);
+
+ /* power on */
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_2000_US, AW88399_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(AW88399_1000_US, AW88399_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 == AW88399_DEV_DSP_WORK) {
+ ret = aw_dev_hw_crc_check(aw88399);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp crc check failed");
+ goto crc_check_fail;
+ }
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_set_vcalb(aw88399);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+
+ 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);
+
+ if (aw88399->dither_st == AW88399_DITHER_EN_ENABLE_VALUE)
+ aw88399_dev_set_dither(aw88399, true);
+
+ /* close mute */
+ aw88399_dev_mute(aw_dev, false);
+ /* clear inturrupt */
+ aw_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88399_DEV_PW_ON;
+
+ return 0;
+
+dsp_check_fail:
+crc_check_fail:
+ aw_dev_dsp_enable(aw_dev, false);
+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 = AW88399_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len, unsigned short base)
+{
+ u32 tmp_len;
+ int i, ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, base);
+ if (ret)
+ goto error_operation;
+
+ for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) {
+ tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE);
+ ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG,
+ &data[i], tmp_len);
+ if (ret)
+ goto error_operation;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+
+error_operation:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return ret;
+}
+
+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, AW88399_DSP_REG_CFG_ADPZ_RA,
+ &dsp_ra, AW88399_DSP_32_DATA);
+ if (ret) {
+ dev_err(aw_dev->dev, "read ra error");
+ return ret;
+ }
+
+ cali_desc->ra = AW88399_DSP_RE_TO_SHOW_RE(dsp_ra,
+ AW88399_DSP_RE_SHIFT);
+
+ 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;
+ }
+
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_CFG_ADDR);
+ if (ret)
+ return ret;
+
+ aw_dev->dsp_cfg_len = len;
+
+ ret = aw_dev_get_ra(&aw_dev->cali_desc);
+
+ return ret;
+}
+
+static int aw_dev_dsp_update_fw(struct aw_device *aw_dev,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ 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_fw_len = len;
+ ret = aw_dev_dsp_update_container(aw_dev, data, len, AW88399_DSP_FW_ADDR);
+
+ return ret;
+}
+
+static int aw_dev_check_sram(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ /* read dsp_rom_check_reg */
+ aw_dev_dsp_read_16bit(aw_dev, AW88399_DSP_ROM_CHECK_ADDR, &reg_val);
+ if (reg_val != AW88399_DSP_ROM_CHECK_DATA) {
+ dev_err(aw_dev->dev, "check dsp rom failed, read[0x%x] != check[0x%x]",
+ reg_val, AW88399_DSP_ROM_CHECK_DATA);
+ goto error;
+ }
+
+ /* check dsp_cfg_base_addr */
+ aw_dev_dsp_write_16bit(aw_dev, AW88399_DSP_CFG_ADDR, AW88399_DSP_ODD_NUM_BIT_TEST);
+ aw_dev_dsp_read_16bit(aw_dev, AW88399_DSP_CFG_ADDR, &reg_val);
+ if (reg_val != AW88399_DSP_ODD_NUM_BIT_TEST) {
+ dev_err(aw_dev->dev, "check dsp cfg failed, read[0x%x] != write[0x%x]",
+ reg_val, AW88399_DSP_ODD_NUM_BIT_TEST);
+ goto error;
+ }
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return 0;
+error:
+ mutex_unlock(&aw_dev->dsp_lock);
+ return -EPERM;
+}
+
+static void aw_dev_select_memclk(struct aw_device *aw_dev, unsigned char flag)
+{
+ int ret;
+
+ switch (flag) {
+ case AW88399_DEV_MEMCLK_PLL:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_DAPHCLK_VALUE);
+ if (ret)
+ dev_err(aw_dev->dev, "memclk select pll failed");
+ break;
+ case AW88399_DEV_MEMCLK_OSC:
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW88399_MEM_CLKSEL_MASK,
+ AW88399_MEM_CLKSEL_OSCCLK_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 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, AW88399_SYSCTRL_REG, &reg_val);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "%s failed", __func__);
+ return;
+ }
+ if ((reg_val & (~AW88399_RCV_MODE_MASK)) == AW88399_RCV_MODE_RECEIVER_VALUE)
+ profctrl_desc->cur_mode = AW88399_RCV_MODE;
+ else
+ profctrl_desc->cur_mode = AW88399_NOT_RCV_MODE;
+}
+
+static int aw_dev_update_reg_container(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ u16 read_vol, reg_val;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u8 reg_addr;
+
+ 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 == AW88399_DSPVCALB_REG) {
+ aw88399->vcalb_init_val = reg_val;
+ continue;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL_REG) {
+ if (reg_val & (~AW88399_DSPBY_MASK))
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_BYPASS;
+ else
+ aw_dev->dsp_cfg = AW88399_DEV_DSP_WORK;
+
+ reg_val &= (AW88399_HMUTE_MASK | AW88399_PWDN_MASK |
+ AW88399_DSPBY_MASK);
+ reg_val |= (AW88399_HMUTE_ENABLE_VALUE | AW88399_PWDN_POWER_DOWN_VALUE |
+ AW88399_DSPBY_BYPASS_VALUE);
+ }
+
+ if (reg_addr == AW88399_I2SCTRL3_REG) {
+ reg_val &= AW88399_I2STXEN_MASK;
+ reg_val |= AW88399_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88399_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88399_VOL_MASK)) >>
+ AW88399_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume = read_vol;
+ }
+
+ if (reg_addr == AW88399_DBGCTRL_REG) {
+ if ((reg_val & (~AW88399_EF_DBMD_MASK)) == AW88399_EF_DBMD_OR_VALUE)
+ aw88399->check_val = AW_EF_OR_CHECK;
+ else
+ aw88399->check_val = AW_EF_AND_CHECK;
+
+ aw88399->dither_st = reg_val & (~AW88399_DITHER_EN_MASK);
+ }
+
+ if (reg_addr == AW88399_CRCCTRL_REG)
+ aw88399->crc_init_val = reg_val;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ return ret;
+ }
+
+ aw_dev_pwd(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+
+ aw_dev_get_cur_mode_st(aw_dev);
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+ else
+ aw_dev_set_volume(aw_dev, vol_desc->ctl_volume);
+
+ return 0;
+}
+
+static int aw_dev_reg_update(struct aw88399 *aw88399,
+ unsigned char *data, unsigned int len)
+{
+ int ret;
+
+ if (!len || !data) {
+ dev_err(aw88399->aw_pa->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ ret = aw_dev_update_reg_container(aw88399, data, len);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "reg update failed");
+
+ return ret;
+}
+
+static int aw88399_dev_get_prof_name(struct aw_device *aw_dev, int index, char **prof_name)
+{
+ 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 -EINVAL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ *prof_name = prof_info->prof_name_list[prof_desc->id];
+
+ return 0;
+}
+
+static int aw88399_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 aw88399_dev_fw_update(struct aw88399 *aw88399, bool up_dsp_fw_en, bool force_up_en)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ 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 == AW88399_FORCE_UPDATE_OFF)) {
+ dev_dbg(aw_dev->dev, "scene no change, not update");
+ return 0;
+ }
+
+ if (aw_dev->fw_status == AW88399_DEV_FW_FAILED) {
+ dev_err(aw_dev->dev, "fw status[%d] error", aw_dev->fw_status);
+ return -EPERM;
+ }
+
+ ret = aw88399_dev_get_prof_name(aw_dev, aw_dev->prof_index, &prof_name);
+ if (ret)
+ return ret;
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88399_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(aw88399, 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;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ if (aw_dev->dsp_cfg == AW88399_DEV_DSP_WORK)
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+
+ ret = aw_dev_check_sram(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "check sram failed");
+ goto error;
+ }
+
+ if (up_dsp_fw_en) {
+ 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, AW88399_DEV_MEMCLK_PLL);
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return 0;
+
+error:
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ return ret;
+}
+
+static void aw88399_start_pa(struct aw88399 *aw88399)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88399_START_RETRIES; i++) {
+ ret = aw88399_dev_start(aw88399);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "aw88399 device start failed. retry = %d", i);
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_ON, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed");
+ continue;
+ }
+ } else {
+ dev_dbg(aw88399->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88399_startup_work(struct work_struct *work)
+{
+ struct aw88399 *aw88399 =
+ container_of(work, struct aw88399, start_work.work);
+
+ mutex_lock(&aw88399->lock);
+ aw88399_start_pa(aw88399);
+ mutex_unlock(&aw88399->lock);
+}
+
+static void aw88399_start(struct aw88399 *aw88399, bool sync_start)
+{
+ int ret;
+
+ if (aw88399->aw_pa->fw_status != AW88399_DEV_FW_OK)
+ return;
+
+ if (aw88399->aw_pa->status == AW88399_DEV_PW_ON)
+ return;
+
+ ret = aw88399_dev_fw_update(aw88399, AW88399_DSP_FW_UPDATE_OFF, true);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "fw update failed.");
+ return;
+ }
+
+ if (sync_start == AW88399_SYNC_START)
+ aw88399_start_pa(aw88399);
+ else
+ queue_delayed_work(system_wq,
+ &aw88399->start_work,
+ AW88399_START_WORK_DELAY_MS);
+}
+
+static int aw_dev_check_sysint(struct aw_device *aw_dev)
+{
+ u16 reg_val;
+
+ aw_dev_get_int_status(aw_dev, &reg_val);
+ if (reg_val & AW88399_BIT_SYSINT_CHECK) {
+ dev_err(aw_dev->dev, "pa stop check fail:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int aw88399_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;
+
+ if (aw_dev->status == AW88399_DEV_PW_OFF) {
+ dev_dbg(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88399_DEV_PW_OFF;
+
+ aw88399_dev_mute(aw_dev, true);
+ usleep_range(AW88399_4000_US, AW88399_4000_US + 100);
+
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ int_st = aw_dev_check_sysint(aw_dev);
+
+ aw_dev_dsp_enable(aw_dev, false);
+
+ aw_dev_amppd(aw_dev, true);
+
+ if (int_st) {
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_OSC);
+ aw_dev_dsp_update_fw(aw_dev, dsp_fw->data, dsp_fw->len);
+ aw_dev_dsp_update_cfg(aw_dev, dsp_cfg->data, dsp_cfg->len);
+ aw_dev_select_memclk(aw_dev, AW88399_DEV_MEMCLK_PLL);
+ }
+
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver aw88399_dai[] = {
+ {
+ .name = "aw88399-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88399_RATES,
+ .formats = AW88399_FORMATS,
+ },
+ },
+};
+
+static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result)
+{
+ if (cali_result == CALI_RESULT_ERROR)
+ aw88399_dev_mute(aw_dev, true);
+ else if (cali_result == CALI_RESULT_NORMAL)
+ aw88399_dev_mute(aw_dev, false);
+}
+
+static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev)
+{
+ struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ &cali_cfg->data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ &cali_cfg->data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ &cali_cfg->data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ &cali_cfg->data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev,
+ struct cali_cfg cali_cfg)
+{
+ int ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ cali_cfg.data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ cali_cfg.data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ cali_cfg.data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ cali_cfg.data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en)
+{
+ struct cali_cfg set_cfg;
+ int ret;
+
+ aw_dev_dsp_enable(aw_dev, false);
+ if (cali_en) {
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE);
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW88399_DSP_16_DATA);
+
+ ret = aw_cali_svc_get_cali_cfg(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get cali cfg failed\n");
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ set_cfg.data[0] = 0;
+ set_cfg.data[1] = 0;
+ set_cfg.data[2] = -1;
+ set_cfg.data[3] = 1;
+
+ ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali cfg failed\n");
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ } else {
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ }
+
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+}
+
+static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable)
+{
+ unsigned int reg_val;
+ int ret;
+
+ if (enable) {
+ ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG);
+ return ret;
+ }
+
+ aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK);
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol);
+ }
+
+ return ret;
+}
+
+static void aw_cali_svc_backup_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+ unsigned int reg_val, dsp_val;
+
+ regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, &reg_val);
+ backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK);
+
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ &dsp_val, AW88399_DSP_16_DATA);
+
+ backup_desc->dsp_lp_cfg = dsp_val;
+}
+
+static void aw_cali_svc_recover_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg);
+
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ backup_desc->dsp_lp_cfg, AW88399_DSP_16_DATA);
+}
+
+static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ return ret;
+ }
+
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status error\n");
+ return ret;
+ }
+
+ aw_cali_svc_backup_info(aw_dev);
+ ret = aw_cali_svc_cali_en(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n");
+ return ret;
+ }
+
+ ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true);
+ if (ret) {
+ aw_cali_svc_cali_en(aw_dev, false);
+ return ret;
+ }
+
+ } else {
+ aw_cali_svc_cali_run_dsp_vol(aw_dev, false);
+ aw_cali_svc_recover_info(aw_dev);
+ aw_cali_svc_cali_en(aw_dev, false);
+ }
+
+ return 0;
+}
+
+static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re)
+{
+ uint32_t dsp_re, show_re;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT);
+
+ *re = (uint32_t)(show_re - aw_dev->cali_desc.ra);
+
+ return 0;
+}
+
+static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re)
+{
+ int sum = 0, i;
+
+ for (i = 1; i < data_size - 1; i++)
+ sum += data[i];
+
+ *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM);
+}
+
+static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_data;
+ int ret, i;
+
+ for (i = 0; i < AW_GET_IV_CNT_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, &reg_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG);
+ return ret;
+ }
+
+ reg_data &= (~AW88399_REABS_MASK);
+ if (!reg_data)
+ return 0;
+ msleep(30);
+ }
+
+ dev_err(aw_dev->dev, "IV data abnormal, please check\n");
+
+ return -EINVAL;
+}
+
+static int compare_ints(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev)
+{
+ uint32_t re_temp[AW_CALI_READ_CNT_MAX];
+ uint32_t dsp_re;
+ int ret, i;
+
+ for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) {
+ ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]);
+ if (ret)
+ goto cali_re_fail;
+ msleep(30);
+ }
+
+ sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL);
+
+ aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re);
+
+ ret = aw_cali_svc_get_iv_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get iv data failed");
+ goto cali_re_fail;
+ }
+
+ if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) {
+ dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re);
+ aw_dev->cali_desc.cali_re = dsp_re;
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+
+ return 0;
+ }
+
+ aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL;
+
+ aw_dev->cali_desc.cali_re = dsp_re;
+ dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re);
+
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+
+cali_re_fail:
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+ return -EINVAL;
+}
+
+static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_cali_desc *cali_desc = &aw_dev->cali_desc;
+ int ret;
+
+ if (cali_desc->cali_running) {
+ dev_err(aw_dev->dev, "calibration in progress\n");
+ return -EINVAL;
+ }
+
+ cali_desc->cali_running = true;
+ aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL);
+
+ ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "start cali re failed\n");
+ goto re_mode_err;
+ }
+
+ msleep(1000);
+
+ ret = aw_cali_svc_get_smooth_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "get cali re failed\n");
+
+ aw_cali_svc_cali_re_mode_enable(aw_dev, false);
+
+re_mode_err:
+ cali_desc->cali_running = false;
+
+ return ret;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 aw88399_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;
+}
+
+static int aw88399_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ char *prof_name;
+ int count, ret;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88399->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;
+
+ count = uinfo->value.enumerated.item;
+
+ ret = aw88399_dev_get_prof_name(aw88399->aw_pa, count, &prof_name);
+ if (ret) {
+ strscpy(uinfo->value.enumerated.name, "null");
+ return 0;
+ }
+
+ strscpy(uinfo->value.enumerated.name, prof_name);
+
+ return 0;
+}
+
+static int aw88399_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88399_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ mutex_lock(&aw88399->lock);
+ ret = aw88399_dev_set_profile_index(aw88399->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88399->lock);
+ return 0;
+ }
+
+ if (aw88399->aw_pa->status) {
+ aw88399_stop(aw88399->aw_pa);
+ aw88399_start(aw88399, AW88399_SYNC_START);
+ }
+
+ mutex_unlock(&aw88399->lock);
+
+ return 1;
+}
+
+static int aw88399_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88399_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88399->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;
+ aw_dev_set_volume(aw88399->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88399->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88399_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = 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 (aw88399->aw_pa->fade_step != value) {
+ aw88399->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88399_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_re;
+
+ return 0;
+}
+
+static int aw88399_re_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88399->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 int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch;
+
+ return 0;
+}
+
+static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int aw88399_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* do nothing */
+ return 0;
+}
+
+static int aw88399_calib_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->status && aw_dev->cali_desc.cali_switch)
+ aw_cali_svc_dev_cali_re(aw88399);
+
+ return 0;
+}
+
+static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88399->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;
+ }
+ aw_dev->fade_in_time = AW88399_1000_US / 10;
+ aw_dev->fade_out_time = AW88399_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 = aw88399_dev_fw_update(aw88399, AW88399_FORCE_UPDATE_ON, AW88399_DSP_FW_UPDATE_ON);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88399_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 100);
+
+ /* enable amppd */
+ aw_dev_amppd(aw_dev, true);
+
+ /* close dsp */
+ aw_dev_dsp_enable(aw_dev, false);
+ /* set power down */
+ aw_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88399_request_firmware_file(struct aw88399 *aw88399)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88399->aw_pa->fw_status = AW88399_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88399_ACF_FILE, aw88399->aw_pa->dev);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "request [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ dev_dbg(aw88399->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88399_ACF_FILE, cont ? cont->size : 0);
+
+ aw88399->aw_cfg = devm_kzalloc(aw88399->aw_pa->dev,
+ struct_size(aw88399->aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw88399->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88399->aw_cfg->len = (int)cont->size;
+ memcpy(aw88399->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88399->aw_pa, aw88399->aw_cfg);
+ if (ret) {
+ dev_err(aw88399->aw_pa->dev, "load [%s] failed!", AW88399_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88399->lock);
+ /* aw device init */
+ ret = aw88399_dev_init(aw88399, aw88399->aw_cfg);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88399->lock);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new aw88399_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88399_SYSCTRL2_REG,
+ 6, AW88399_MUTE_VOL, 0, aw88399_volume_get,
+ aw88399_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88399_MUTE_VOL, 0,
+ aw88399_get_fade_step, aw88399_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_in_time, aw88399_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88399_get_fade_out_time, aw88399_set_fade_out_time),
+ SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0,
+ aw88399_re_get, aw88399_re_set),
+ SOC_SINGLE_BOOL_EXT("Calib Switch", 0,
+ aw88399_calib_switch_get, aw88399_calib_switch_set),
+ SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0,
+ aw88399_calib_get, aw88399_calib_set),
+ AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info,
+ aw88399_profile_get, aw88399_profile_set),
+};
+
+static int aw88399_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 aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88399->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88399_start(aw88399, AW88399_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88399_stop(aw88399->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88399->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88399_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88399_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 aw88399_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88399_codec_probe(struct snd_soc_component *component)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88399->start_work, aw88399_startup_work);
+
+ ret = aw88399_request_firmware_file(aw88399);
+ if (ret)
+ dev_err(aw88399->aw_pa->dev, "%s failed\n", __func__);
+
+ return ret;
+}
+
+static void aw88399_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88399->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88399 = {
+ .probe = aw88399_codec_probe,
+ .remove = aw88399_codec_remove,
+ .dapm_widgets = aw88399_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aw88399_dapm_widgets),
+ .dapm_routes = aw88399_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(aw88399_audio_map),
+ .controls = aw88399_controls,
+ .num_controls = ARRAY_SIZE(aw88399_controls),
+};
+
+static void aw88399_hw_reset(struct aw88399 *aw88399)
+{
+ if (aw88399->reset_gpio) {
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 0);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ gpiod_set_value_cansleep(aw88399->reset_gpio, 1);
+ usleep_range(AW88399_1000_US, AW88399_1000_US + 10);
+ }
+}
+
+static void aw88399_parse_channel_dt(struct aw_device *aw_dev)
+{
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value;
+
+ of_property_read_u32(np, "awinic,audio-channel", &channel_value);
+ aw_dev->channel = channel_value;
+}
+
+static int aw88399_init(struct aw88399 *aw88399, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_read(regmap, AW88399_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88399_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+ dev_dbg(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+ aw88399->aw_pa = aw_dev;
+
+ aw_dev->i2c = i2c;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->regmap = regmap;
+ mutex_init(&aw_dev->dsp_lock);
+
+ aw_dev->chip_id = 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 = AW88399_DEV_DEFAULT_CH;
+ aw_dev->fw_status = AW88399_DEV_FW_FAILED;
+
+ aw_dev->fade_step = AW88399_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88399_VOL_DEFAULT_VALUE;
+
+ aw88399_parse_channel_dt(aw_dev);
+
+ return 0;
+}
+
+static int aw88399_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88399 *aw88399;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88399 = devm_kzalloc(&i2c->dev, sizeof(*aw88399), GFP_KERNEL);
+ if (!aw88399)
+ return -ENOMEM;
+
+ mutex_init(&aw88399->lock);
+
+ i2c_set_clientdata(i2c, aw88399);
+
+ aw88399->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88399->reset_gpio))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->reset_gpio),
+ "reset gpio not defined\n");
+ aw88399_hw_reset(aw88399);
+
+ aw88399->regmap = devm_regmap_init_i2c(i2c, &aw88399_remap_config);
+ if (IS_ERR(aw88399->regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(aw88399->regmap),
+ "failed to init regmap\n");
+
+ /* aw pa init */
+ ret = aw88399_init(aw88399, i2c, aw88399->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88399,
+ aw88399_dai, ARRAY_SIZE(aw88399_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88399: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88399_i2c_id[] = {
+ { AW88399_I2C_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id aw88399_acpi_match[] = {
+ { "AWDZ8399", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, aw88399_acpi_match);
+#endif
+
+static struct i2c_driver aw88399_i2c_driver = {
+ .driver = {
+ .name = AW88399_I2C_NAME,
+ .acpi_match_table = ACPI_PTR(aw88399_acpi_match),
+ },
+ .probe = aw88399_i2c_probe,
+ .id_table = aw88399_i2c_id,
+};
+module_i2c_driver(aw88399_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88399 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h
new file mode 100644
index 000000000000..cacc03b1eefa
--- /dev/null
+++ b/sound/soc/codecs/aw88399.h
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88399.h -- ALSA SoC AW88399 codec support
+//
+// Copyright (c) 2023 AWINIC Technology CO., LTD
+//
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88399_H__
+#define __AW88399_H__
+
+/* registers list */
+#define AW88399_ID_REG (0x00)
+#define AW88399_SYSST_REG (0x01)
+#define AW88399_SYSINT_REG (0x02)
+#define AW88399_SYSINTM_REG (0x03)
+#define AW88399_SYSCTRL_REG (0x04)
+#define AW88399_SYSCTRL2_REG (0x05)
+#define AW88399_I2SCTRL1_REG (0x06)
+#define AW88399_I2SCTRL2_REG (0x07)
+#define AW88399_I2SCTRL3_REG (0x08)
+#define AW88399_DACCFG1_REG (0x09)
+#define AW88399_DACCFG2_REG (0x0A)
+#define AW88399_DACCFG3_REG (0x0B)
+#define AW88399_DACCFG4_REG (0x0C)
+#define AW88399_DACCFG5_REG (0x0D)
+#define AW88399_DACCFG6_REG (0x0E)
+#define AW88399_DACCFG7_REG (0x0F)
+#define AW88399_MPDCFG1_REG (0x10)
+#define AW88399_MPDCFG2_REG (0x11)
+#define AW88399_MPDCFG3_REG (0x12)
+#define AW88399_MPDCFG4_REG (0x13)
+#define AW88399_PWMCTRL1_REG (0x14)
+#define AW88399_PWMCTRL2_REG (0x15)
+#define AW88399_PWMCTRL3_REG (0x16)
+#define AW88399_I2SCFG1_REG (0x17)
+#define AW88399_DBGCTRL_REG (0x18)
+#define AW88399_HAGCST_REG (0x20)
+#define AW88399_VBAT_REG (0x21)
+#define AW88399_TEMP_REG (0x22)
+#define AW88399_PVDD_REG (0x23)
+#define AW88399_ISNDAT_REG (0x24)
+#define AW88399_VSNDAT_REG (0x25)
+#define AW88399_I2SINT_REG (0x26)
+#define AW88399_I2SCAPCNT_REG (0x27)
+#define AW88399_ANASTA1_REG (0x28)
+#define AW88399_ANASTA2_REG (0x29)
+#define AW88399_ANASTA3_REG (0x2A)
+#define AW88399_TESTDET_REG (0x2B)
+#define AW88399_DSMCFG1_REG (0x30)
+#define AW88399_DSMCFG2_REG (0x31)
+#define AW88399_DSMCFG3_REG (0x32)
+#define AW88399_DSMCFG4_REG (0x33)
+#define AW88399_DSMCFG5_REG (0x34)
+#define AW88399_DSMCFG6_REG (0x35)
+#define AW88399_DSMCFG7_REG (0x36)
+#define AW88399_DSMCFG8_REG (0x37)
+#define AW88399_TESTIN_REG (0x38)
+#define AW88399_TESTOUT_REG (0x39)
+#define AW88399_MEMTEST_REG (0x3A)
+#define AW88399_VSNCTRL1_REG (0x3B)
+#define AW88399_ISNCTRL1_REG (0x3C)
+#define AW88399_ISNCTRL2_REG (0x3D)
+#define AW88399_DSPMADD_REG (0x40)
+#define AW88399_DSPMDAT_REG (0x41)
+#define AW88399_WDT_REG (0x42)
+#define AW88399_ACR1_REG (0x43)
+#define AW88399_ACR2_REG (0x44)
+#define AW88399_ASR1_REG (0x45)
+#define AW88399_ASR2_REG (0x46)
+#define AW88399_DSPCFG_REG (0x47)
+#define AW88399_ASR3_REG (0x48)
+#define AW88399_ASR4_REG (0x49)
+#define AW88399_DSPVCALB_REG (0x4A)
+#define AW88399_CRCCTRL_REG (0x4B)
+#define AW88399_DSPDBG1_REG (0x4C)
+#define AW88399_DSPDBG2_REG (0x4D)
+#define AW88399_DSPDBG3_REG (0x4E)
+#define AW88399_PLLCTRL1_REG (0x50)
+#define AW88399_PLLCTRL2_REG (0x51)
+#define AW88399_PLLCTRL3_REG (0x52)
+#define AW88399_CDACTRL1_REG (0x53)
+#define AW88399_CDACTRL2_REG (0x54)
+#define AW88399_CDACTRL3_REG (0x55)
+#define AW88399_SADCCTRL1_REG (0x56)
+#define AW88399_SADCCTRL2_REG (0x57)
+#define AW88399_BOPCTRL1_REG (0x58)
+#define AW88399_BOPCTRL2_REG (0x5A)
+#define AW88399_BOPCTRL3_REG (0x5B)
+#define AW88399_BOPCTRL4_REG (0x5C)
+#define AW88399_BOPCTRL5_REG (0x5D)
+#define AW88399_BOPCTRL6_REG (0x5E)
+#define AW88399_BOPCTRL7_REG (0x5F)
+#define AW88399_BSTCTRL1_REG (0x60)
+#define AW88399_BSTCTRL2_REG (0x61)
+#define AW88399_BSTCTRL3_REG (0x62)
+#define AW88399_BSTCTRL4_REG (0x63)
+#define AW88399_BSTCTRL5_REG (0x64)
+#define AW88399_BSTCTRL6_REG (0x65)
+#define AW88399_BSTCTRL7_REG (0x66)
+#define AW88399_BSTCTRL8_REG (0x67)
+#define AW88399_BSTCTRL9_REG (0x68)
+#define AW88399_BSTCTRL10_REG (0x69)
+#define AW88399_CPCTRL_REG (0x6A)
+#define AW88399_EFWH_REG (0x6C)
+#define AW88399_EFWM2_REG (0x6D)
+#define AW88399_EFWM1_REG (0x6E)
+#define AW88399_EFWL_REG (0x6F)
+#define AW88399_TESTCTRL1_REG (0x70)
+#define AW88399_TESTCTRL2_REG (0x71)
+#define AW88399_EFCTRL1_REG (0x72)
+#define AW88399_EFCTRL2_REG (0x73)
+#define AW88399_EFRH4_REG (0x74)
+#define AW88399_EFRH3_REG (0x75)
+#define AW88399_EFRH2_REG (0x76)
+#define AW88399_EFRH1_REG (0x77)
+#define AW88399_EFRL4_REG (0x78)
+#define AW88399_EFRL3_REG (0x79)
+#define AW88399_EFRL2_REG (0x7A)
+#define AW88399_EFRL1_REG (0x7B)
+#define AW88399_TM_REG (0x7C)
+#define AW88399_TM2_REG (0x7D)
+
+#define AW88399_REG_MAX (0x7E)
+#define AW88399_MUTE_VOL (1023)
+
+#define AW88399_DSP_CFG_ADDR (0x9B00)
+#define AW88399_DSP_REG_CFG_ADPZ_RA (0x9B68)
+#define AW88399_DSP_FW_ADDR (0x8980)
+#define AW88399_DSP_ROM_CHECK_ADDR (0x1F40)
+#define AW88399_DSP_ROM_CHECK_DATA (0x4638)
+
+#define AW88399_CALI_RE_HBITS_MASK (~(0xFFFF0000))
+#define AW88399_CALI_RE_HBITS_SHIFT (16)
+
+#define AW88399_CALI_RE_LBITS_MASK (~(0xFFFF))
+#define AW88399_CALI_RE_LBITS_SHIFT (0)
+
+#define AW88399_I2STXEN_START_BIT (9)
+#define AW88399_I2STXEN_BITS_LEN (1)
+#define AW88399_I2STXEN_MASK \
+ (~(((1<<AW88399_I2STXEN_BITS_LEN)-1) << AW88399_I2STXEN_START_BIT))
+
+#define AW88399_I2STXEN_DISABLE (0)
+#define AW88399_I2STXEN_DISABLE_VALUE \
+ (AW88399_I2STXEN_DISABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_I2STXEN_ENABLE (1)
+#define AW88399_I2STXEN_ENABLE_VALUE \
+ (AW88399_I2STXEN_ENABLE << AW88399_I2STXEN_START_BIT)
+
+#define AW88399_VOL_START_BIT (0)
+#define AW88399_VOL_BITS_LEN (10)
+#define AW88399_VOL_MASK \
+ (~(((1<<AW88399_VOL_BITS_LEN)-1) << AW88399_VOL_START_BIT))
+
+#define AW88399_PWDN_START_BIT (0)
+#define AW88399_PWDN_BITS_LEN (1)
+#define AW88399_PWDN_MASK \
+ (~(((1<<AW88399_PWDN_BITS_LEN)-1) << AW88399_PWDN_START_BIT))
+
+#define AW88399_PWDN_POWER_DOWN (1)
+#define AW88399_PWDN_POWER_DOWN_VALUE \
+ (AW88399_PWDN_POWER_DOWN << AW88399_PWDN_START_BIT)
+
+#define AW88399_PWDN_WORKING (0)
+#define AW88399_PWDN_WORKING_VALUE \
+ (AW88399_PWDN_WORKING << AW88399_PWDN_START_BIT)
+
+#define AW88399_DSPBY_START_BIT (2)
+#define AW88399_DSPBY_BITS_LEN (1)
+#define AW88399_DSPBY_MASK \
+ (~(((1<<AW88399_DSPBY_BITS_LEN)-1) << AW88399_DSPBY_START_BIT))
+
+#define AW88399_DSPBY_WORKING (0)
+#define AW88399_DSPBY_WORKING_VALUE \
+ (AW88399_DSPBY_WORKING << AW88399_DSPBY_START_BIT)
+
+#define AW88399_DSPBY_BYPASS (1)
+#define AW88399_DSPBY_BYPASS_VALUE \
+ (AW88399_DSPBY_BYPASS << AW88399_DSPBY_START_BIT)
+
+#define AW88399_MEM_CLKSEL_START_BIT (3)
+#define AW88399_MEM_CLKSEL_BITS_LEN (1)
+#define AW88399_MEM_CLKSEL_MASK \
+ (~(((1<<AW88399_MEM_CLKSEL_BITS_LEN)-1) << AW88399_MEM_CLKSEL_START_BIT))
+
+#define AW88399_MEM_CLKSEL_OSCCLK (0)
+#define AW88399_MEM_CLKSEL_OSCCLK_VALUE \
+ (AW88399_MEM_CLKSEL_OSCCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_MEM_CLKSEL_DAPHCLK (1)
+#define AW88399_MEM_CLKSEL_DAPHCLK_VALUE \
+ (AW88399_MEM_CLKSEL_DAPHCLK << AW88399_MEM_CLKSEL_START_BIT)
+
+#define AW88399_DITHER_EN_START_BIT (15)
+#define AW88399_DITHER_EN_BITS_LEN (1)
+#define AW88399_DITHER_EN_MASK \
+ (~(((1<<AW88399_DITHER_EN_BITS_LEN)-1) << AW88399_DITHER_EN_START_BIT))
+
+#define AW88399_DITHER_EN_DISABLE (0)
+#define AW88399_DITHER_EN_DISABLE_VALUE \
+ (AW88399_DITHER_EN_DISABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_DITHER_EN_ENABLE (1)
+#define AW88399_DITHER_EN_ENABLE_VALUE \
+ (AW88399_DITHER_EN_ENABLE << AW88399_DITHER_EN_START_BIT)
+
+#define AW88399_HMUTE_START_BIT (8)
+#define AW88399_HMUTE_BITS_LEN (1)
+#define AW88399_HMUTE_MASK \
+ (~(((1<<AW88399_HMUTE_BITS_LEN)-1) << AW88399_HMUTE_START_BIT))
+
+#define AW88399_HMUTE_DISABLE (0)
+#define AW88399_HMUTE_DISABLE_VALUE \
+ (AW88399_HMUTE_DISABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_HMUTE_ENABLE (1)
+#define AW88399_HMUTE_ENABLE_VALUE \
+ (AW88399_HMUTE_ENABLE << AW88399_HMUTE_START_BIT)
+
+#define AW88399_EF_DBMD_START_BIT (2)
+#define AW88399_EF_DBMD_BITS_LEN (1)
+#define AW88399_EF_DBMD_MASK \
+ (~(((1<<AW88399_EF_DBMD_BITS_LEN)-1) << AW88399_EF_DBMD_START_BIT))
+
+#define AW88399_EF_DBMD_OR (1)
+#define AW88399_EF_DBMD_OR_VALUE \
+ (AW88399_EF_DBMD_OR << AW88399_EF_DBMD_START_BIT)
+
+#define AW88399_VDSEL_START_BIT (5)
+#define AW88399_VDSEL_BITS_LEN (1)
+#define AW88399_VDSEL_MASK \
+ (~(((1<<AW88399_VDSEL_BITS_LEN)-1) << AW88399_VDSEL_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_H_START_BIT))
+
+/* EF_VSN_GESLP_H bit 9:0 (EFRH3 0x75) */
+#define AW88399_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88399_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88399_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88399_EF_ISN_GESLP_L_START_BIT))
+
+/* EF_VSN_GESLP_L bit 9:0 (EFRL3 0x79) */
+#define AW88399_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88399_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88399_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88399_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88399_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_H_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_H_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_H_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_H_START_BIT))
+
+#define AW88399_INTERNAL_VSN_TRIM_L_START_BIT (9)
+#define AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN (6)
+#define AW88399_INTERNAL_VSN_TRIM_L_MASK \
+ (~(((1<<AW88399_INTERNAL_VSN_TRIM_L_BITS_LEN)-1) << AW88399_INTERNAL_VSN_TRIM_L_START_BIT))
+
+#define AW88399_RCV_MODE_START_BIT (7)
+#define AW88399_RCV_MODE_BITS_LEN (1)
+#define AW88399_RCV_MODE_MASK \
+ (~(((1<<AW88399_RCV_MODE_BITS_LEN)-1) << AW88399_RCV_MODE_START_BIT))
+
+#define AW88399_CLKI_START_BIT (4)
+#define AW88399_NOCLKI_START_BIT (5)
+#define AW88399_PLLI_START_BIT (0)
+#define AW88399_PLLI_INT_VALUE (1)
+#define AW88399_PLLI_INT_INTERRUPT \
+ (AW88399_PLLI_INT_VALUE << AW88399_PLLI_START_BIT)
+
+#define AW88399_CLKI_INT_VALUE (1)
+#define AW88399_CLKI_INT_INTERRUPT \
+ (AW88399_CLKI_INT_VALUE << AW88399_CLKI_START_BIT)
+
+#define AW88399_NOCLKI_INT_VALUE (1)
+#define AW88399_NOCLKI_INT_INTERRUPT \
+ (AW88399_NOCLKI_INT_VALUE << AW88399_NOCLKI_START_BIT)
+
+#define AW88399_BIT_SYSINT_CHECK \
+ (AW88399_PLLI_INT_INTERRUPT | \
+ AW88399_CLKI_INT_INTERRUPT | \
+ AW88399_NOCLKI_INT_INTERRUPT)
+
+#define AW88399_CRC_CHECK_START_BIT (12)
+#define AW88399_CRC_CHECK_BITS_LEN (3)
+#define AW88399_CRC_CHECK_BITS_MASK \
+ (~(((1<<AW88399_CRC_CHECK_BITS_LEN)-1) << AW88399_CRC_CHECK_START_BIT))
+
+#define AW88399_RCV_MODE_RECEIVER (1)
+#define AW88399_RCV_MODE_RECEIVER_VALUE \
+ (AW88399_RCV_MODE_RECEIVER << AW88399_RCV_MODE_START_BIT)
+
+#define AW88399_AMPPD_START_BIT (1)
+#define AW88399_AMPPD_BITS_LEN (1)
+#define AW88399_AMPPD_MASK \
+ (~(((1<<AW88399_AMPPD_BITS_LEN)-1) << AW88399_AMPPD_START_BIT))
+
+#define AW88399_AMPPD_WORKING (0)
+#define AW88399_AMPPD_WORKING_VALUE \
+ (AW88399_AMPPD_WORKING << AW88399_AMPPD_START_BIT)
+
+#define AW88399_AMPPD_POWER_DOWN (1)
+#define AW88399_AMPPD_POWER_DOWN_VALUE \
+ (AW88399_AMPPD_POWER_DOWN << AW88399_AMPPD_START_BIT)
+
+#define AW88399_RAM_CG_BYP_START_BIT (0)
+#define AW88399_RAM_CG_BYP_BITS_LEN (1)
+#define AW88399_RAM_CG_BYP_MASK \
+ (~(((1<<AW88399_RAM_CG_BYP_BITS_LEN)-1) << AW88399_RAM_CG_BYP_START_BIT))
+
+#define AW88399_RAM_CG_BYP_WORK (0)
+#define AW88399_RAM_CG_BYP_WORK_VALUE \
+ (AW88399_RAM_CG_BYP_WORK << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_RAM_CG_BYP_BYPASS (1)
+#define AW88399_RAM_CG_BYP_BYPASS_VALUE \
+ (AW88399_RAM_CG_BYP_BYPASS << AW88399_RAM_CG_BYP_START_BIT)
+
+#define AW88399_CRC_END_ADDR_START_BIT (0)
+#define AW88399_CRC_END_ADDR_BITS_LEN (12)
+#define AW88399_CRC_END_ADDR_MASK \
+ (~(((1<<AW88399_CRC_END_ADDR_BITS_LEN)-1) << AW88399_CRC_END_ADDR_START_BIT))
+
+#define AW88399_CRC_CODE_EN_START_BIT (13)
+#define AW88399_CRC_CODE_EN_BITS_LEN (1)
+#define AW88399_CRC_CODE_EN_MASK \
+ (~(((1<<AW88399_CRC_CODE_EN_BITS_LEN)-1) << AW88399_CRC_CODE_EN_START_BIT))
+
+#define AW88399_CRC_CODE_EN_DISABLE (0)
+#define AW88399_CRC_CODE_EN_DISABLE_VALUE \
+ (AW88399_CRC_CODE_EN_DISABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CODE_EN_ENABLE (1)
+#define AW88399_CRC_CODE_EN_ENABLE_VALUE \
+ (AW88399_CRC_CODE_EN_ENABLE << AW88399_CRC_CODE_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_START_BIT (12)
+#define AW88399_CRC_CFG_EN_BITS_LEN (1)
+#define AW88399_CRC_CFG_EN_MASK \
+ (~(((1<<AW88399_CRC_CFG_EN_BITS_LEN)-1) << AW88399_CRC_CFG_EN_START_BIT))
+
+#define AW88399_CRC_CFG_EN_DISABLE (0)
+#define AW88399_CRC_CFG_EN_DISABLE_VALUE \
+ (AW88399_CRC_CFG_EN_DISABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_CRC_CFG_EN_ENABLE (1)
+#define AW88399_CRC_CFG_EN_ENABLE_VALUE \
+ (AW88399_CRC_CFG_EN_ENABLE << AW88399_CRC_CFG_EN_START_BIT)
+
+#define AW88399_OCDS_START_BIT (3)
+#define AW88399_OCDS_OC (1)
+#define AW88399_OCDS_OC_VALUE \
+ (AW88399_OCDS_OC << AW88399_OCDS_START_BIT)
+
+#define AW88399_NOCLKS_START_BIT (5)
+#define AW88399_NOCLKS_NO_CLOCK (1)
+#define AW88399_NOCLKS_NO_CLOCK_VALUE \
+ (AW88399_NOCLKS_NO_CLOCK << AW88399_NOCLKS_START_BIT)
+
+#define AW88399_SWS_START_BIT (8)
+#define AW88399_SWS_SWITCHING (1)
+#define AW88399_SWS_SWITCHING_VALUE \
+ (AW88399_SWS_SWITCHING << AW88399_SWS_START_BIT)
+
+#define AW88399_BSTS_START_BIT (9)
+#define AW88399_BSTS_FINISHED (1)
+#define AW88399_BSTS_FINISHED_VALUE \
+ (AW88399_BSTS_FINISHED << AW88399_BSTS_START_BIT)
+
+#define AW88399_UVLS_START_BIT (14)
+#define AW88399_UVLS_NORMAL (0)
+#define AW88399_UVLS_NORMAL_VALUE \
+ (AW88399_UVLS_NORMAL << AW88399_UVLS_START_BIT)
+
+#define AW88399_BSTOCS_START_BIT (11)
+#define AW88399_BSTOCS_OVER_CURRENT (1)
+#define AW88399_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88399_BSTOCS_OVER_CURRENT << AW88399_BSTOCS_START_BIT)
+
+#define AW88399_OTHS_START_BIT (1)
+#define AW88399_OTHS_OT (1)
+#define AW88399_OTHS_OT_VALUE \
+ (AW88399_OTHS_OT << AW88399_OTHS_START_BIT)
+
+#define AW88399_PLLS_START_BIT (0)
+#define AW88399_PLLS_LOCKED (1)
+#define AW88399_PLLS_LOCKED_VALUE \
+ (AW88399_PLLS_LOCKED << AW88399_PLLS_START_BIT)
+
+#define AW88399_CLKS_START_BIT (4)
+#define AW88399_CLKS_STABLE (1)
+#define AW88399_CLKS_STABLE_VALUE \
+ (AW88399_CLKS_STABLE << AW88399_CLKS_START_BIT)
+
+#define AW88399_BIT_PLL_CHECK \
+ (AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_CHECK_MASK \
+ (~(AW88399_UVLS_NORMAL_VALUE | \
+ AW88399_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE | \
+ AW88399_NOCLKS_NO_CLOCK_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_OCDS_OC_VALUE | \
+ AW88399_OTHS_OT_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE))
+
+#define AW88399_BIT_SYSST_NOSWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE)
+
+#define AW88399_BIT_SYSST_SWS_CHECK \
+ (AW88399_BSTS_FINISHED_VALUE | \
+ AW88399_CLKS_STABLE_VALUE | \
+ AW88399_PLLS_LOCKED_VALUE | \
+ AW88399_SWS_SWITCHING_VALUE)
+
+#define AW88399_CCO_MUX_START_BIT (14)
+#define AW88399_CCO_MUX_BITS_LEN (1)
+#define AW88399_CCO_MUX_MASK \
+ (~(((1<<AW88399_CCO_MUX_BITS_LEN)-1) << AW88399_CCO_MUX_START_BIT))
+
+#define AW88399_CCO_MUX_DIVIDED (0)
+#define AW88399_CCO_MUX_DIVIDED_VALUE \
+ (AW88399_CCO_MUX_DIVIDED << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_CCO_MUX_BYPASS (1)
+#define AW88399_CCO_MUX_BYPASS_VALUE \
+ (AW88399_CCO_MUX_BYPASS << AW88399_CCO_MUX_START_BIT)
+
+#define AW88399_NOISE_GATE_EN_START_BIT (13)
+#define AW88399_NOISE_GATE_EN_BITS_LEN (1)
+#define AW88399_NOISE_GATE_EN_MASK \
+ (~(((1<<AW88399_NOISE_GATE_EN_BITS_LEN)-1) << AW88399_NOISE_GATE_EN_START_BIT))
+
+#define AW88399_WDT_CNT_START_BIT (0)
+#define AW88399_WDT_CNT_BITS_LEN (8)
+#define AW88399_WDT_CNT_MASK \
+ (~(((1<<AW88399_WDT_CNT_BITS_LEN)-1) << AW88399_WDT_CNT_START_BIT))
+
+#define AW88399_REABS_START_BIT (3)
+#define AW88399_REABS_BITS_LEN (1)
+#define AW88399_REABS_MASK \
+ (~(((1<<AW88399_REABS_BITS_LEN)-1) << AW88399_REABS_START_BIT))
+
+#define AW88399_DSP_VOL_START_BIT (8)
+#define AW88399_DSP_VOL_BITS_LEN (8)
+#define AW88399_DSP_VOL_MASK \
+ (~(((1<<AW88399_DSP_VOL_BITS_LEN)-1) << AW88399_DSP_VOL_START_BIT))
+
+#define AW883XX_DSP_NG_EN_START (13)
+#define AW883XX_DSP_NG_EN_LEN (1)
+#define AW883XX_DSP_NG_EN_MASK \
+ (~(((1 << AW883XX_DSP_NG_EN_LEN) - 1) << AW883XX_DSP_NG_EN_START))
+#define AW883XX_DSP_NG_EN_DISABLE (0)
+#define AW883XX_DSP_NG_EN_DISABLE_VALUE \
+ (AW883XX_DSP_NG_EN_DISABLE << AW883XX_DSP_NG_EN_START)
+
+#define AW88399_VOLUME_STEP_DB (64)
+#define AW88399_VOL_DEFAULT_VALUE (0)
+#define AW88399_DSP_ODD_NUM_BIT_TEST (0x5555)
+#define AW88399_EF_ISN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_ISN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_EF_VSN_GESLP_SIGN_MASK (~(1 << 9))
+#define AW88399_EF_VSN_GESLP_SIGN_NEG (0xfe00)
+
+#define AW88399_TEM4_SIGN_MASK (~(1 << 5))
+#define AW88399_TEM4_SIGN_NEG (0xffc0)
+
+#define AW88399_ICABLK_FACTOR (1)
+#define AW88399_VCABLK_FACTOR (1)
+#define AW88399_VCABLK_DAC_FACTOR (2)
+
+#define AW88399_VCALB_ADJ_FACTOR (12)
+#define AW88399_VCALB_ACCURACY (1 << 12)
+
+#define AW88399_ISCAL_FACTOR (3125)
+#define AW88399_VSCAL_FACTOR (18875)
+#define AW88399_ISCAL_DAC_FACTOR (3125)
+#define AW88399_VSCAL_DAC_FACTOR (12600)
+#define AW88399_CABL_BASE_VALUE (1000)
+
+#define AW88399_DEV_DEFAULT_CH (0)
+#define AW88399_DEV_DSP_CHECK_MAX (5)
+#define AW88399_MAX_RAM_WRITE_BYTE_SIZE (128)
+#define AW88399_DSP_RE_SHIFT (12)
+#define AW88399_CALI_RE_MAX (15000)
+#define AW88399_CALI_RE_MIN (4000)
+#define AW_FW_ADDR_LEN (4)
+#define AW88399_DSP_RE_TO_SHOW_RE(re, shift) (((re) * (1000)) >> (shift))
+#define AW88399_SHOW_RE_TO_DSP_RE(re, shift) (((re) << shift) / (1000))
+#define AW88399_CRC_CHECK_PASS_VAL (0x4)
+
+#define AW88399_CRC_CFG_BASE_ADDR (0xD80)
+#define AW88399_CRC_FW_BASE_ADDR (0x4C0)
+#define AW88399_ACF_FILE "aw88399_acf.bin"
+#define AW88399_DEV_SYSST_CHECK_MAX (10)
+#define AW88399_CHIP_ID 0x2183
+
+#define AW88399_I2C_NAME "aw88399"
+
+#define AW88399_START_RETRIES (5)
+#define AW88399_START_WORK_DELAY_MS (0)
+
+#define AW88399_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88399_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 AW_CALI_READ_CNT_MAX (8)
+#define AW88399_DSP_REG_CALRE (0x8141)
+#define AW88399_DSP_REG_CALRE_SHIFT (10)
+#define AW_CALI_DATA_SUM_RM (2)
+
+#define AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH (0x9B4C)
+#define AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH (0x9B4E)
+#define AW88399_DSP_REG_CFG_ADPZ_USTEPN (0x9B6E)
+#define AW88399_DSP_REG_CFG_RE_ALPHA (0x9BD4)
+#define AW_GET_IV_CNT_MAX (6)
+
+#define AW88399_DSP_VOL_MUTE (0XFF00)
+
+#define AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR (0x9BEC)
+#define AW88399_DSP_LOW_POWER_SWITCH_DISABLE (0x110b)
+
+#define AW88399_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 {
+ AW_EF_AND_CHECK = 0,
+ AW_EF_OR_CHECK,
+};
+
+enum {
+ AW88399_DEV_VDSEL_DAC = 0,
+ AW88399_DEV_VDSEL_VSENSE = 32,
+};
+
+enum {
+ AW88399_DSP_CRC_NA = 0,
+ AW88399_DSP_CRC_OK = 1,
+};
+
+enum {
+ AW88399_DSP_FW_UPDATE_OFF = 0,
+ AW88399_DSP_FW_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_FORCE_UPDATE_OFF = 0,
+ AW88399_FORCE_UPDATE_ON = 1,
+};
+
+enum {
+ AW88399_1000_US = 1000,
+ AW88399_2000_US = 2000,
+ AW88399_3000_US = 3000,
+ AW88399_4000_US = 4000,
+};
+
+enum AW88399_DEV_STATUS {
+ AW88399_DEV_PW_OFF = 0,
+ AW88399_DEV_PW_ON,
+};
+
+enum AW88399_DEV_FW_STATUS {
+ AW88399_DEV_FW_FAILED = 0,
+ AW88399_DEV_FW_OK,
+};
+
+enum AW88399_DEV_MEMCLK {
+ AW88399_DEV_MEMCLK_OSC = 0,
+ AW88399_DEV_MEMCLK_PLL = 1,
+};
+
+enum AW88399_DEV_DSP_CFG {
+ AW88399_DEV_DSP_WORK = 0,
+ AW88399_DEV_DSP_BYPASS = 1,
+};
+
+enum {
+ AW88399_DSP_16_DATA = 0,
+ AW88399_DSP_32_DATA = 1,
+};
+
+enum {
+ AW88399_NOT_RCV_MODE = 0,
+ AW88399_RCV_MODE = 1,
+};
+
+enum {
+ AW88399_SYNC_START = 0,
+ AW88399_ASYNC_START,
+};
+
+struct aw88399 {
+ 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;
+
+ unsigned int check_val;
+ unsigned int crc_init_val;
+ unsigned int vcalb_init_val;
+ unsigned int dither_st;
+};
+
+#endif
diff --git a/sound/soc/codecs/bd28623.c b/sound/soc/codecs/bd28623.c
new file mode 100644
index 000000000000..82a94211d012
--- /dev/null
+++ b/sound/soc/codecs/bd28623.c
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ROHM BD28623MUV class D speaker amplifier codec driver.
+//
+// Copyright (c) 2018 Socionext Inc.
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+#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 <suzuki.katsuhiro@socionext.com>");
+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
index e7238b8904bc..3afcef2dfa35 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -1,12 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for generic Bluetooth SCO link
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -18,52 +13,72 @@
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[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
+ { "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,
+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 struct snd_soc_codec_driver soc_codec_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),
+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 snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
- &bt_sco_dai, 1);
-}
-
-static int bt_sco_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_bt_sco,
+ bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
-static struct platform_device_id bt_sco_driver_ids[] = {
+static const struct platform_device_id bt_sco_driver_ids[] = {
{
.name = "dfbmcs320",
},
@@ -74,12 +89,21 @@ static struct platform_device_id bt_sco_driver_ids[] = {
};
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,
- .remove = bt_sco_remove,
.id_table = bt_sco_driver_ids,
};
diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c
new file mode 100644
index 000000000000..40020500b1fe
--- /dev/null
+++ b/sound/soc/codecs/chv3-codec.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/module.h>
+#include <sound/soc.h>
+
+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", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, chv3_codec_of_match);
+
+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 <pan@semihalf.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cirrus_legacy.h b/sound/soc/codecs/cirrus_legacy.h
new file mode 100644
index 000000000000..87c6fd79290d
--- /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 000000000000..3eb862643b53
--- /dev/null
+++ b/sound/soc/codecs/cpcap.c
@@ -0,0 +1,1887 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC CPCAP codec driver
+ *
+ * Copyright (C) 2017 - 2018 Sebastian Reichel <sre@kernel.org>
+ *
+ * Very loosely based on original driver from Motorola:
+ * Copyright (C) 2007 - 2009 Motorola, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/motorola-cpcap.h>
+#include <sound/core.h>
+#include <linux/input.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* Register 8 - CPCAP_REG_INTS1 --- Interrupt Sense 1 */
+#define CPCAP_BIT_HS_S 9 /* Headset */
+#define CPCAP_BIT_MB2_S 10 /* Mic Bias2 */
+
+/* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */
+#define CPCAP_BIT_PTT_S 11 /* Push To Talk */
+
+/* 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;
+ struct regulator *vaudio;
+ int hsirq;
+ int mb2irq;
+ struct snd_soc_jack jack;
+};
+
+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, &reg_voice);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXSDOA, &reg_hifi);
+ if (err)
+ return err;
+ err = regmap_read(cpcap->regmap, CPCAP_REG_RXEPOA, &reg_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, &regval);
+ 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, &regval);
+ 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 irqreturn_t cpcap_hs_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mask = SND_JACK_HEADSET;
+ int val;
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S))) {
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_TXI, val, val);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_RXOA, val, val);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+
+ /* Give PTTS time to settle */
+ msleep(20);
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS2,
+ BIT(CPCAP_BIT_PTT_S))) {
+ /* Headphones detected. (May also be a headset with the
+ * MFB pressed.)
+ */
+ status = SND_JACK_HEADPHONE;
+ dev_info(component->dev, "HP plugged in\n");
+ } else if (regmap_test_bits(regmap, CPCAP_REG_INTS1,
+ BIT(CPCAP_BIT_MB2_S)) == 1) {
+ status = SND_JACK_HEADSET;
+ dev_info(component->dev, "HS plugged in\n");
+ } else
+ dev_info(component->dev, "Unsupported HS plugged in\n");
+ } else {
+ bool mic = cpcap->jack.status & SND_JACK_MICROPHONE;
+
+ dev_info(component->dev, "H%s disconnect\n", mic ? "S" : "P");
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, val, 0);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, val, 0);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+
+ mask |= SND_JACK_BTN_0;
+ }
+
+ snd_soc_jack_report(&cpcap->jack, status, mask);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_mb2_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mb2;
+ int ptt;
+
+ if (regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S)) == 1)
+ return IRQ_HANDLED;
+
+ mb2 = regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_MB2_S));
+ ptt = regmap_test_bits(regmap, CPCAP_REG_INTS2, BIT(CPCAP_BIT_PTT_S));
+
+ /* Initial detection might have been with MFB pressed */
+ if (!(cpcap->jack.status & SND_JACK_MICROPHONE)) {
+ if (ptt == 1 && mb2 == 1) {
+ dev_info(component->dev, "MIC plugged in\n");
+ snd_soc_jack_report(&cpcap->jack, SND_JACK_MICROPHONE,
+ SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ if (!mb2 || !ptt)
+ status = SND_JACK_BTN_0;
+
+ snd_soc_jack_report(&cpcap->jack, status, SND_JACK_BTN_0);
+
+ return IRQ_HANDLED;
+}
+
+static int cpcap_soc_probe(struct snd_soc_component *component)
+{
+ struct platform_device *pdev = to_platform_device(component->dev);
+ struct snd_soc_card *card = component->card;
+ 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->vaudio = devm_regulator_get(component->dev, "VAUDIO");
+ if (IS_ERR(cpcap->vaudio))
+ return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio),
+ "Cannot get VAUDIO regulator\n");
+
+ err = snd_soc_card_jack_new(card, "Headphones",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &cpcap->jack);
+ if (err < 0) {
+ dev_err(component->dev, "Cannot create HS jack: %i\n", err);
+ return err;
+ }
+
+ snd_jack_set_key(cpcap->jack.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
+ 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;
+
+ cpcap->hsirq = platform_get_irq_byname(pdev, "hs");
+ if (cpcap->hsirq < 0)
+ return cpcap->hsirq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL,
+ cpcap_hs_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-hs",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no HS irq%i: %i\n",
+ cpcap->hsirq, err);
+ return err;
+ }
+
+ cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2");
+ if (cpcap->mb2irq < 0)
+ return cpcap->mb2irq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL,
+ cpcap_mb2_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-mb2",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no MB2 irq%i: %i\n",
+ cpcap->mb2irq, err);
+ return err;
+ }
+
+ err = cpcap_audio_reset(component, false);
+ if (err)
+ return err;
+
+ cpcap_hs_irq_thread(cpcap->hsirq, component);
+
+ enable_irq_wake(cpcap->hsirq);
+ enable_irq_wake(cpcap->mb2irq);
+
+ return 0;
+}
+
+static void cpcap_soc_remove(struct snd_soc_component *component)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ disable_irq_wake(cpcap->hsirq);
+ disable_irq_wake(cpcap->mb2irq);
+}
+
+static int cpcap_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ /* VAIDIO should be kept in normal mode in order MIC/PTT to work */
+ if (cpcap->jack.status & SND_JACK_MICROPHONE)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cpcap = {
+ .probe = cpcap_soc_probe,
+ .remove = cpcap_soc_remove,
+ .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),
+ .set_bias_level = cpcap_set_bias_level,
+ .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
index d6dedd4eab29..32b6a417d0e8 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -1,23 +1,10 @@
+// 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 <miguel.aguilar@ridgerun.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -43,9 +30,9 @@ static const struct snd_kcontrol_new cq93vc_snd_controls[] = {
SOC_SINGLE("Mono DAC Playback Volume", DAVINCI_VC_REG09, 0, 0x3f, 0),
};
-static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
+static int cq93vc_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u8 reg;
if (mute)
@@ -53,7 +40,7 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
+ snd_soc_component_update_bits(component, DAVINCI_VC_REG09, DAVINCI_VC_REG09_MUTE,
reg);
return 0;
@@ -72,27 +59,26 @@ static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
-static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
+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_write(codec, DAVINCI_VC_REG12,
+ 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_write(codec, DAVINCI_VC_REG12,
+ 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_write(codec, DAVINCI_VC_REG12,
+ snd_soc_component_write(component, DAVINCI_VC_REG12,
DAVINCI_VC_REG12_POWER_ALL_OFF);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -101,8 +87,9 @@ static int cq93vc_set_bias_level(struct snd_soc_codec *codec,
#define CQ93VC_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
static const struct snd_soc_dai_ops cq93vc_dai_ops = {
- .digital_mute = cq93vc_mute,
+ .mute_stream = cq93vc_mute,
.set_sysclk = cq93vc_set_dai_sysclk,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cq93vc_dai = {
@@ -122,30 +109,29 @@ static struct snd_soc_dai_driver cq93vc_dai = {
.ops = &cq93vc_dai_ops,
};
-static struct regmap *cq93vc_get_regmap(struct device *dev)
+static int cq93vc_probe(struct snd_soc_component *component)
{
- struct davinci_vc *davinci_vc = dev->platform_data;
+ struct davinci_vc *davinci_vc = component->dev->platform_data;
- return davinci_vc->regmap;
+ snd_soc_component_init_regmap(component, davinci_vc->regmap);
+
+ return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
- .set_bias_level = cq93vc_set_bias_level,
- .get_regmap = cq93vc_get_regmap,
- .controls = cq93vc_snd_controls,
- .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_cq93vc, &cq93vc_dai, 1);
-}
-
-static int cq93vc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_cq93vc, &cq93vc_dai, 1);
}
static struct platform_driver cq93vc_codec_driver = {
@@ -154,7 +140,6 @@ static struct platform_driver cq93vc_codec_driver = {
},
.probe = cq93vc_platform_probe,
- .remove = cq93vc_platform_remove,
};
module_platform_driver(cq93vc_codec_driver);
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
new file mode 100644
index 000000000000..937c8cec682a
--- /dev/null
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -0,0 +1,1066 @@
+// 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 <crypto/sha2.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+#include <linux/string_choices.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+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",
+ str_enable_disable(enabled));
+ 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 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);
+ }
+
+ ret = of_reserved_mem_region_to_resource(dev->of_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);
+ }
+#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 <cychiang@chromium.org>");
+MODULE_ALIAS("platform:cros-ec-codec");
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
new file mode 100644
index 000000000000..2fde84309338
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -0,0 +1,1002 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// KUnit test for the Cirrus common amplifier library.
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <kunit/resource.h>
+#include <kunit/test.h>
+#include <kunit/static_stub.h>
+#include <linux/device/faux.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <sound/cs-amp-lib.h>
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
+struct cs_amp_lib_test_priv {
+ struct faux_device *amp_dev;
+
+ struct cirrus_amp_efi_data *cal_blob;
+ struct list_head ctl_write_list;
+};
+
+struct cs_amp_lib_test_ctl_write_entry {
+ struct list_head list;
+ unsigned int value;
+ char name[16];
+};
+
+struct cs_amp_lib_test_param {
+ int num_amps;
+ int amp_index;
+};
+
+static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ unsigned int blob_size;
+ int i;
+
+ blob_size = struct_size(priv->cal_blob, data, num_amps);
+
+ priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
+
+ priv->cal_blob->size = blob_size;
+ priv->cal_blob->count = num_amps;
+
+ get_random_bytes(priv->cal_blob->data, flex_array_size(priv->cal_blob, data, num_amps));
+
+ /* Ensure all timestamps are non-zero to mark the entry valid. */
+ for (i = 0; i < num_amps; i++)
+ priv->cal_blob->data[i].calTime[0] |= 1;
+
+ /* Ensure that all UIDs are non-zero and unique. */
+ for (i = 0; i < num_amps; i++)
+ *(u8 *)&priv->cal_blob->data[i].calTarget[0] = i + 1;
+}
+
+static u64 cs_amp_lib_test_get_target_uid(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ u64 uid;
+
+ uid = priv->cal_blob->data[param->amp_index].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[param->amp_index].calTarget[0];
+
+ return uid;
+}
+
+/* Redirected get_efi_variable to simulate that the file is too short */
+static efi_status_t cs_amp_lib_test_get_efi_variable_nohead(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ if (!buf) {
+ *size = offsetof(struct cirrus_amp_efi_data, data) - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/* Should return -EOVERFLOW if the header is larger than the EFI data */
+static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_nohead);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the count is larger than the file */
+static efi_status_t cs_amp_lib_test_get_efi_variable_bad_count(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ if (!buf) {
+ /*
+ * Return a size that is shorter than required for the
+ * declared number of entries.
+ */
+ *size = priv->cal_blob->size - 1;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size - 1);
+
+ return EFI_SUCCESS;
+}
+
+/* Should return -EOVERFLOW if the entry count is larger than the EFI data */
+static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_bad_count);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate that the variable not found */
+static efi_status_t cs_amp_lib_test_get_efi_variable_none(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_NOT_FOUND;
+}
+
+/* If EFI doesn't contain a cal data variable the result should be -ENOENT */
+static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* Redirected get_efi_variable to simulate reading a cal data blob */
+static efi_status_t cs_amp_lib_test_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"CirrusSmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_hp_cal_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ static const efi_char16_t expected_name[] = L"SmartAmpCalibrationData";
+ static const efi_guid_t expected_guid =
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93);
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, name);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, guid);
+ KUNIT_EXPECT_NOT_ERR_OR_NULL(test, size);
+
+ if (memcmp(name, expected_name, sizeof(expected_name)) ||
+ efi_guidcmp(*guid, expected_guid))
+ return -EFI_NOT_FOUND;
+
+ if (!buf) {
+ *size = priv->cal_blob->size;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ KUNIT_ASSERT_GE_MSG(test, ksize(buf), priv->cal_blob->size, "Buffer to small");
+
+ memcpy(buf, priv->cal_blob, priv->cal_blob->size);
+
+ return EFI_SUCCESS;
+}
+
+/* Get cal data block from HP variable. */
+static void cs_amp_lib_test_get_hp_efi_cal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 2);
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_hp_cal_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ KUNIT_EXPECT_MEMEQ(test, &result_data, &priv->cal_blob->data[0], sizeof(result_data));
+}
+
+/* Get cal data block for a given amp, matched by target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[0], target_uid & 0xFFFFFFFFULL);
+ KUNIT_EXPECT_EQ(test, result_data.calTarget[1], target_uid >> 32);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index without checking target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/* Get cal data block for a given amp index with checked target UID. */
+static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ target_uid = cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * Get cal data block for a given amp index with checked target UID.
+ * The UID does not match so the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ u64 target_uid;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Get a target UID that won't match the entry */
+ target_uid = ~cs_amp_lib_test_get_target_uid(test);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * Get cal data block for a given amp, where the cal data does not
+ * specify calTarget so the lookup falls back to using the index
+ */
+static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ const struct cs_amp_lib_test_param *param = test->param_value;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, param->num_amps);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid,
+ param->amp_index, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+
+ KUNIT_EXPECT_EQ(test, result_data.calTime[0],
+ priv->cal_blob->data[param->amp_index].calTime[0]);
+ KUNIT_EXPECT_EQ(test, result_data.calTime[1],
+ priv->cal_blob->data[param->amp_index].calTime[1]);
+ KUNIT_EXPECT_EQ(test, result_data.calAmbient,
+ priv->cal_blob->data[param->amp_index].calAmbient);
+ KUNIT_EXPECT_EQ(test, result_data.calStatus,
+ priv->cal_blob->data[param->amp_index].calStatus);
+ KUNIT_EXPECT_EQ(test, result_data.calR,
+ priv->cal_blob->data[param->amp_index].calR);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and there isn't an
+ * index to fall back do, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, -1,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't present in the cal data, and the index is
+ * out of range, the result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ static const u64 bad_target_uid = 0xBADCA100BABABABAULL;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values != bad_target_uid */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] &= ~(bad_target_uid & 0xFFFFFFFFULL);
+ priv->cal_blob->data[i].calTarget[1] &= ~(bad_target_uid >> 32);
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, 99,
+ &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the target UID isn't given, and the index is out of range, the
+ * result should be -ENOENT.
+ */
+static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 99, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/* If neither the target UID or the index is given the result should be -ENOENT. */
+static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If the UID is passed as 0 this must not match an entry with an
+ * unpopulated calTarget
+ */
+static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ int i, ret;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Make all the target values zero so they are ignored */
+ for (i = 0; i < priv->cal_blob->count; ++i) {
+ priv->cal_blob->data[i].calTarget[0] = 0;
+ priv->cal_blob->data[i].calTarget[1] = 0;
+ }
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
+ KUNIT_EXPECT_EQ(test, ret, -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+/*
+ * If an entry has a timestamp of 0 it should be ignored even if it has
+ * a matching target UID.
+ */
+static void cs_amp_lib_test_get_efi_cal_empty_entry_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cirrus_amp_cal_data result_data;
+ u64 uid;
+
+ cs_amp_lib_test_init_dummy_cal_blob(test, 8);
+
+ /* Mark the 3rd entry invalid by zeroing calTime */
+ priv->cal_blob->data[2].calTime[0] = 0;
+ priv->cal_blob->data[2].calTime[1] = 0;
+
+ /* Get the UID value of the 3rd entry */
+ uid = priv->cal_blob->data[2].calTarget[1];
+ uid <<= 32;
+ uid |= priv->cal_blob->data[2].calTarget[0];
+
+ /* Redirect calls to get EFI data */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable);
+
+ /* Lookup by UID should not find it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ uid, -1,
+ &result_data),
+ -ENOENT);
+
+ /* Get by index should ignore it */
+ KUNIT_EXPECT_EQ(test,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
+ 0, 2,
+ &result_data),
+ -ENOENT);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
+}
+
+static const struct cirrus_amp_cal_controls cs_amp_lib_test_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+static int cs_amp_lib_test_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctl_name);
+ KUNIT_EXPECT_PTR_EQ(test, controls, &cs_amp_lib_test_calibration_controls);
+
+ entry = kunit_kzalloc(test, sizeof(*entry), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, entry);
+
+ INIT_LIST_HEAD(&entry->list);
+ strscpy(entry->name, ctl_name, sizeof(entry->name));
+ entry->value = val;
+
+ list_add_tail(&entry->list, &priv->ctl_write_list);
+
+ return 0;
+}
+
+static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct cs_amp_lib_test_ctl_write_entry *entry;
+ struct cirrus_amp_cal_data data;
+ struct cs_dsp *dsp;
+ int ret;
+
+ dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
+ dsp->dev = &priv->amp_dev->dev;
+
+ get_random_bytes(&data, sizeof(data));
+
+ /* Redirect calls to write firmware controls */
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->write_cal_coeff,
+ cs_amp_lib_test_write_cal_coeff);
+
+ ret = cs_amp_write_cal_coeffs(dsp, &cs_amp_lib_test_calibration_controls, &data);
+ KUNIT_EXPECT_EQ(test, ret, 0);
+
+ kunit_deactivate_static_stub(test, cs_amp_test_hooks->write_cal_coeff);
+
+ KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->ctl_write_list), 4);
+
+ /* Checksum control must be written last */
+ entry = list_last_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.checksum);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR + 1);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.ambient);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calAmbient);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.calr);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calR);
+ list_del(&entry->list);
+
+ entry = list_first_entry(&priv->ctl_write_list, typeof(*entry), list);
+ KUNIT_EXPECT_STREQ(test, entry->name, cs_amp_lib_test_calibration_controls.status);
+ KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
+}
+
+static void cs_amp_lib_test_spkid_lenovo_not_present(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_none);
+
+ KUNIT_EXPECT_EQ(test, -ENOENT, cs_amp_get_vendor_spkid(dev));
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d0(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd0;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_d1(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, LENOVO_SPEAKER_ID_EFI_NAME, sizeof(LENOVO_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0xd1;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_lenovo_00(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ KUNIT_ASSERT_EQ(test, 0, efi_guidcmp(*guid, LENOVO_SPEAKER_ID_EFI_GUID));
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d0(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d0);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_d1(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_d1);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_lenovo_illegal(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_lenovo_00);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_buf_too_small(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ return EFI_BUFFER_TOO_SMALL;
+}
+
+static void cs_amp_lib_test_spkid_lenovo_oversize(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_buf_too_small);
+
+ KUNIT_EXPECT_LT(test, cs_amp_get_vendor_spkid(dev), 0);
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_30(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x30;
+
+ return EFI_SUCCESS;
+}
+
+static efi_status_t cs_amp_lib_test_get_efi_variable_hp_31(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ struct kunit *test = kunit_get_current_test();
+
+ if (efi_guidcmp(*guid, HP_SPEAKER_ID_EFI_GUID) ||
+ memcmp(name, HP_SPEAKER_ID_EFI_NAME, sizeof(HP_SPEAKER_ID_EFI_NAME)))
+ return EFI_NOT_FOUND;
+
+ KUNIT_ASSERT_EQ(test, *size, 1);
+ *size = 1;
+ *(u8 *)buf = 0x31;
+
+ return EFI_SUCCESS;
+}
+
+static void cs_amp_lib_test_spkid_hp_30(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_30);
+
+ KUNIT_EXPECT_EQ(test, 0, cs_amp_get_vendor_spkid(dev));
+}
+
+static void cs_amp_lib_test_spkid_hp_31(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv = test->priv;
+ struct device *dev = &priv->amp_dev->dev;
+
+ kunit_activate_static_stub(test,
+ cs_amp_test_hooks->get_efi_variable,
+ cs_amp_lib_test_get_efi_variable_hp_31);
+
+ KUNIT_EXPECT_EQ(test, 1, cs_amp_get_vendor_spkid(dev));
+}
+
+static int cs_amp_lib_test_case_init(struct kunit *test)
+{
+ struct cs_amp_lib_test_priv *priv;
+
+ KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
+
+ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ test->priv = priv;
+ INIT_LIST_HEAD(&priv->ctl_write_list);
+
+ /* Create dummy amp driver dev */
+ priv->amp_dev = faux_device_create("cs_amp_lib_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
+
+ return 0;
+}
+
+static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
+ { .num_amps = 2, .amp_index = 0 },
+ { .num_amps = 2, .amp_index = 1 },
+
+ { .num_amps = 3, .amp_index = 0 },
+ { .num_amps = 3, .amp_index = 1 },
+ { .num_amps = 3, .amp_index = 2 },
+
+ { .num_amps = 4, .amp_index = 0 },
+ { .num_amps = 4, .amp_index = 1 },
+ { .num_amps = 4, .amp_index = 2 },
+ { .num_amps = 4, .amp_index = 3 },
+
+ { .num_amps = 5, .amp_index = 0 },
+ { .num_amps = 5, .amp_index = 1 },
+ { .num_amps = 5, .amp_index = 2 },
+ { .num_amps = 5, .amp_index = 3 },
+ { .num_amps = 5, .amp_index = 4 },
+
+ { .num_amps = 6, .amp_index = 0 },
+ { .num_amps = 6, .amp_index = 1 },
+ { .num_amps = 6, .amp_index = 2 },
+ { .num_amps = 6, .amp_index = 3 },
+ { .num_amps = 6, .amp_index = 4 },
+ { .num_amps = 6, .amp_index = 5 },
+
+ { .num_amps = 8, .amp_index = 0 },
+ { .num_amps = 8, .amp_index = 1 },
+ { .num_amps = 8, .amp_index = 2 },
+ { .num_amps = 8, .amp_index = 3 },
+ { .num_amps = 8, .amp_index = 4 },
+ { .num_amps = 8, .amp_index = 5 },
+ { .num_amps = 8, .amp_index = 6 },
+ { .num_amps = 8, .amp_index = 7 },
+};
+
+static void cs_amp_lib_test_get_cal_param_desc(const struct cs_amp_lib_test_param *param,
+ char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "num_amps:%d amp_index:%d",
+ param->num_amps, param->amp_index);
+}
+
+KUNIT_ARRAY_PARAM(cs_amp_lib_test_get_cal, cs_amp_lib_test_get_cal_param_cases,
+ cs_amp_lib_test_get_cal_param_desc);
+
+static struct kunit_case cs_amp_lib_test_cases[] = {
+ /* Tests for getting calibration data from EFI */
+ KUNIT_CASE(cs_amp_lib_test_cal_data_too_short_test),
+ KUNIT_CASE(cs_amp_lib_test_cal_count_too_big_test),
+ KUNIT_CASE(cs_amp_lib_test_no_cal_data_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_no_uid_no_index_test),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_zero_not_matched_test),
+ KUNIT_CASE(cs_amp_lib_test_get_hp_efi_cal),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_uid_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_unchecked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_checked_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE_PARAM(cs_amp_lib_test_get_efi_cal_by_index_fallback_test,
+ cs_amp_lib_test_get_cal_gen_params),
+ KUNIT_CASE(cs_amp_lib_test_get_efi_cal_empty_entry_test),
+
+ /* Tests for writing calibration data */
+ KUNIT_CASE(cs_amp_lib_test_write_cal_data_test),
+
+ /* Test cases for speaker ID */
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_not_present),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d0),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_d1),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_illegal),
+ KUNIT_CASE(cs_amp_lib_test_spkid_lenovo_oversize),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_30),
+ KUNIT_CASE(cs_amp_lib_test_spkid_hp_31),
+
+ { } /* terminator */
+};
+
+static struct kunit_suite cs_amp_lib_test_suite = {
+ .name = "snd-soc-cs-amp-lib-test",
+ .init = cs_amp_lib_test_case_init,
+ .test_cases = cs_amp_lib_test_cases,
+};
+
+kunit_test_suite(cs_amp_lib_test_suite);
+
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_DESCRIPTION("KUnit test for Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
new file mode 100644
index 000000000000..8434d5196107
--- /dev/null
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Common code for Cirrus Logic Smart Amplifiers
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <asm/byteorder.h>
+#include <kunit/static_stub.h>
+#include <linux/dev_printk.h>
+#include <linux/efi.h>
+#include <linux/firmware/cirrus/cs_dsp.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#define CIRRUS_LOGIC_CALIBRATION_EFI_NAME L"CirrusSmartAmpCalibrationData"
+#define CIRRUS_LOGIC_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d, 0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3)
+
+#define LENOVO_SPEAKER_ID_EFI_NAME L"SdwSpeaker"
+#define LENOVO_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0x48df970e, 0xe27f, 0x460a, 0xb5, 0x86, 0x77, 0x19, 0x80, 0x1d, 0x92, 0x82)
+
+#define HP_SPEAKER_ID_EFI_NAME L"HPSpeakerID"
+#define HP_SPEAKER_ID_EFI_GUID \
+ EFI_GUID(0xc49593a4, 0xd099, 0x419b, 0xa2, 0xc3, 0x67, 0xe9, 0x80, 0xe6, 0x1d, 0x1e)
+
+#define HP_CALIBRATION_EFI_NAME L"SmartAmpCalibrationData"
+#define HP_CALIBRATION_EFI_GUID \
+ EFI_GUID(0x53559579, 0x8753, 0x4f5c, 0x91, 0x30, 0xe8, 0x2a, 0xcf, 0xb8, 0xd8, 0x93)
+
+static const struct cs_amp_lib_cal_efivar {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+} cs_amp_lib_cal_efivars[] = {
+ {
+ .name = HP_CALIBRATION_EFI_NAME,
+ .guid = &HP_CALIBRATION_EFI_GUID,
+ },
+ {
+ .name = CIRRUS_LOGIC_CALIBRATION_EFI_NAME,
+ .guid = &CIRRUS_LOGIC_CALIBRATION_EFI_GUID,
+ },
+};
+
+static int cs_amp_write_cal_coeff(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const char *ctl_name, u32 val)
+{
+ struct cs_dsp_coeff_ctl *cs_ctl;
+ __be32 beval = cpu_to_be32(val);
+ int ret;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_write_cal_coeff, dsp, controls, ctl_name, val);
+
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP)) {
+ mutex_lock(&dsp->pwr_lock);
+ cs_ctl = cs_dsp_get_ctl(dsp, ctl_name, controls->mem_region, controls->alg_id);
+ ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, &beval, sizeof(beval));
+ mutex_unlock(&dsp->pwr_lock);
+
+ if (ret < 0) {
+ dev_err(dsp->dev, "Failed to write to '%s': %d\n", ctl_name, ret);
+ return ret;
+ }
+
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int _cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ int ret;
+
+ dev_dbg(dsp->dev, "Calibration: Ambient=%#x, Status=%#x, CalR=%d\n",
+ data->calAmbient, data->calStatus, data->calR);
+
+ if (list_empty(&dsp->ctl_list)) {
+ dev_info(dsp->dev, "Calibration disabled due to missing firmware controls\n");
+ return -ENOENT;
+ }
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->ambient, data->calAmbient);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->calr, data->calR);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->status, data->calStatus);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeff(dsp, controls, controls->checksum, data->calR + 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * cs_amp_write_cal_coeffs - Write calibration data to firmware controls.
+ * @dsp: Pointer to struct cs_dsp.
+ * @controls: Pointer to definition of firmware controls to be written.
+ * @data: Pointer to calibration data.
+ *
+ * Returns: 0 on success, else negative error value.
+ */
+int cs_amp_write_cal_coeffs(struct cs_dsp *dsp,
+ const struct cirrus_amp_cal_controls *controls,
+ const struct cirrus_amp_cal_data *data)
+{
+ if (IS_REACHABLE(CONFIG_FW_CS_DSP) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_write_cal_coeffs(dsp, controls, data);
+ else
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_write_cal_coeffs, "SND_SOC_CS_AMP_LIB");
+
+static efi_status_t cs_amp_get_efi_variable(efi_char16_t *name,
+ efi_guid_t *guid,
+ unsigned long *size,
+ void *buf)
+{
+ u32 attr;
+
+ KUNIT_STATIC_STUB_REDIRECT(cs_amp_get_efi_variable, name, guid, size, buf);
+
+ if (efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE))
+ return efi.get_variable(name, guid, &attr, size, buf);
+
+ return EFI_NOT_FOUND;
+}
+
+static int cs_amp_convert_efi_status(efi_status_t status)
+{
+ switch (status) {
+ case EFI_SUCCESS:
+ return 0;
+ case EFI_NOT_FOUND:
+ return -ENOENT;
+ case EFI_BUFFER_TOO_SMALL:
+ return -EFBIG;
+ case EFI_UNSUPPORTED:
+ case EFI_ACCESS_DENIED:
+ case EFI_SECURITY_VIOLATION:
+ return -EACCES;
+ default:
+ return -EIO;
+ }
+}
+
+static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
+{
+ struct cirrus_amp_efi_data *efi_data;
+ unsigned long data_size = 0;
+ u8 *data;
+ efi_status_t status;
+ int i, ret;
+
+ /* Find EFI variable and get size */
+ for (i = 0; i < ARRAY_SIZE(cs_amp_lib_cal_efivars); i++) {
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, NULL);
+ if (status == EFI_BUFFER_TOO_SMALL)
+ break;
+ }
+
+ if (status != EFI_BUFFER_TOO_SMALL)
+ return ERR_PTR(-ENOENT);
+
+ if (data_size < sizeof(*efi_data)) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ return ERR_PTR(-EOVERFLOW);
+ }
+
+ /* Get variable contents into buffer */
+ data = kmalloc(data_size, GFP_KERNEL);
+ if (!data)
+ return ERR_PTR(-ENOMEM);
+
+ status = cs_amp_get_efi_variable(cs_amp_lib_cal_efivars[i].name,
+ cs_amp_lib_cal_efivars[i].guid,
+ &data_size, data);
+ if (status != EFI_SUCCESS) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ efi_data = (struct cirrus_amp_efi_data *)data;
+ dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
+
+ if ((efi_data->count > 128) ||
+ struct_size(efi_data, data, efi_data->count) > data_size) {
+ dev_err(dev, "EFI cal variable truncated\n");
+ ret = -EOVERFLOW;
+ goto err;
+ }
+
+ return efi_data;
+
+err:
+ kfree(data);
+ dev_err(dev, "Failed to read calibration data from EFI: %d\n", ret);
+
+ return ERR_PTR(ret);
+}
+
+static u64 cs_amp_cal_target_u64(const struct cirrus_amp_cal_data *data)
+{
+ return ((u64)data->calTarget[1] << 32) | data->calTarget[0];
+}
+
+static int _cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ struct cirrus_amp_efi_data *efi_data;
+ struct cirrus_amp_cal_data *cal = NULL;
+ int i, ret;
+
+ efi_data = cs_amp_get_cal_efi_buffer(dev);
+ if (IS_ERR(efi_data))
+ return PTR_ERR(efi_data);
+
+ if (target_uid) {
+ for (i = 0; i < efi_data->count; ++i) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[i]);
+
+ /* Skip empty entries */
+ if (!efi_data->data[i].calTime[0] && !efi_data->data[i].calTime[1])
+ continue;
+
+ /* Skip entries with unpopulated silicon ID */
+ if (cal_target == 0)
+ continue;
+
+ if (cal_target == target_uid) {
+ cal = &efi_data->data[i];
+ break;
+ }
+ }
+ }
+
+ if (!cal && (amp_index >= 0) && (amp_index < efi_data->count) &&
+ (efi_data->data[amp_index].calTime[0] || efi_data->data[amp_index].calTime[1])) {
+ u64 cal_target = cs_amp_cal_target_u64(&efi_data->data[amp_index]);
+
+ /*
+ * Treat unpopulated cal_target as a wildcard.
+ * If target_uid != 0 we can only get here if cal_target == 0
+ * or it didn't match any cal_target value.
+ * If target_uid == 0 it is a wildcard.
+ */
+ if ((cal_target == 0) || (target_uid == 0))
+ cal = &efi_data->data[amp_index];
+ else
+ dev_warn(dev, "Calibration entry %d does not match silicon ID", amp_index);
+ }
+
+ if (cal) {
+ memcpy(out_data, cal, sizeof(*out_data));
+ ret = 0;
+ } else {
+ dev_warn(dev, "No calibration for silicon ID %#llx\n", target_uid);
+ ret = -ENOENT;
+ }
+
+ kfree(efi_data);
+
+ return ret;
+}
+
+/**
+ * cs_amp_get_efi_calibration_data - get an entry from calibration data in EFI.
+ * @dev: struct device of the caller.
+ * @target_uid: UID to match, or zero to ignore UID matching.
+ * @amp_index: Entry index to use, or -1 to prevent lookup by index.
+ * @out_data: struct cirrus_amp_cal_data where the entry will be copied.
+ *
+ * This function can perform 3 types of lookup:
+ *
+ * (target_uid > 0, amp_index >= 0)
+ * UID search with fallback to using the array index.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else, if the entry at
+ * [amp_index] has calTarget == 0, return that entry. Else fail.
+ *
+ * (target_uid > 0, amp_index < 0)
+ * UID search only.
+ * Search the calibration data for a non-zero calTarget that matches
+ * target_uid, and if found return that entry. Else fail.
+ *
+ * (target_uid == 0, amp_index >= 0)
+ * Array index fetch only.
+ * Return the entry at [amp_index].
+ *
+ * An array lookup will be skipped if amp_index exceeds the number of
+ * entries in the calibration array, and in this case the return will
+ * be -ENOENT. An out-of-range amp_index does not prevent matching by
+ * target_uid - it has the same effect as passing amp_index < 0.
+ *
+ * If the EFI data is too short to be a valid entry, or the entry count
+ * in the EFI data overflows the actual length of the data, this function
+ * returns -EOVERFLOW.
+ *
+ * Return: 0 if the entry was found, -ENOENT if no entry was found,
+ * -EOVERFLOW if the EFI file is corrupt, else other error value.
+ */
+int cs_amp_get_efi_calibration_data(struct device *dev, u64 target_uid, int amp_index,
+ struct cirrus_amp_cal_data *out_data)
+{
+ if (IS_ENABLED(CONFIG_EFI) || IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return _cs_amp_get_efi_calibration_data(dev, target_uid, amp_index, out_data);
+ else
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_efi_calibration_data, "SND_SOC_CS_AMP_LIB");
+
+struct cs_amp_spkid_efi {
+ efi_char16_t *name;
+ efi_guid_t *guid;
+ u8 values[2];
+};
+
+static int cs_amp_get_efi_byte_spkid(struct device *dev, const struct cs_amp_spkid_efi *info)
+{
+ efi_status_t status;
+ unsigned long size;
+ u8 spkid;
+ int i, ret;
+
+ size = sizeof(spkid);
+ status = cs_amp_get_efi_variable(info->name, info->guid, &size, &spkid);
+ ret = cs_amp_convert_efi_status(status);
+ if (ret < 0)
+ return ret;
+
+ if (size == 0)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(info->values); i++) {
+ if (info->values[i] == spkid)
+ return i;
+ }
+
+ dev_err(dev, "EFI speaker ID bad value %#x\n", spkid);
+
+ return -EINVAL;
+}
+
+static const struct cs_amp_spkid_efi cs_amp_spkid_byte_types[] = {
+ {
+ .name = LENOVO_SPEAKER_ID_EFI_NAME,
+ .guid = &LENOVO_SPEAKER_ID_EFI_GUID,
+ .values = { 0xd0, 0xd1 },
+ },
+ {
+ .name = HP_SPEAKER_ID_EFI_NAME,
+ .guid = &HP_SPEAKER_ID_EFI_GUID,
+ .values = { 0x30, 0x31 },
+ },
+};
+
+/**
+ * cs_amp_get_vendor_spkid - get a speaker ID from vendor-specific storage
+ * @dev: pointer to struct device
+ *
+ * Known vendor-specific methods of speaker ID are checked and if one is
+ * found its speaker ID value is returned.
+ *
+ * Return: >=0 is a valid speaker ID. -ENOENT if a vendor-specific method
+ * was not found. -EACCES if the vendor-specific storage could not
+ * be read. Other error values indicate that the data from the
+ * vendor-specific storage was found but could not be understood.
+ */
+int cs_amp_get_vendor_spkid(struct device *dev)
+{
+ int i, ret;
+
+ if (!efi_rt_services_supported(EFI_RT_SUPPORTED_GET_VARIABLE) &&
+ !IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST))
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(cs_amp_spkid_byte_types); i++) {
+ ret = cs_amp_get_efi_byte_spkid(dev, &cs_amp_spkid_byte_types[i]);
+ if (ret != -ENOENT)
+ return ret;
+ }
+
+ return -ENOENT;
+}
+EXPORT_SYMBOL_NS_GPL(cs_amp_get_vendor_spkid, "SND_SOC_CS_AMP_LIB");
+
+static const struct cs_amp_test_hooks cs_amp_test_hook_ptrs = {
+ .get_efi_variable = cs_amp_get_efi_variable,
+ .write_cal_coeff = cs_amp_write_cal_coeff,
+};
+
+const struct cs_amp_test_hooks * const cs_amp_test_hooks =
+ PTR_IF(IS_ENABLED(CONFIG_SND_SOC_CS_AMP_LIB_TEST), &cs_amp_test_hook_ptrs);
+EXPORT_SYMBOL_NS_GPL(cs_amp_test_hooks, "SND_SOC_CS_AMP_LIB");
+
+MODULE_DESCRIPTION("Cirrus Logic amplifier library");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index f2b8aad21274..0bb4bdb3deec 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -1,30 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs35l32.c -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -35,6 +29,7 @@
#include <dt-bindings/sound/cs35l32.h>
#include "cs35l32.h"
+#include "cirrus_legacy.h"
#define CS35L32_NUM_SUPPLIES 2
static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
@@ -44,7 +39,7 @@ static const char *const cs35l32_supply_names[CS35L32_NUM_SUPPLIES] = {
struct cs35l32_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct regulator_bulk_data supplies[CS35L32_NUM_SUPPLIES];
struct cs35l32_platform_data pdata;
struct gpio_desc *reset_gpio;
@@ -75,33 +70,8 @@ static const struct reg_default cs35l32_reg_defaults[] = {
static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_DEVID_AB:
- case CS35L32_DEVID_CD:
- case CS35L32_DEVID_E:
- case CS35L32_FAB_ID:
- case CS35L32_REV_ID:
- case CS35L32_PWRCTL1:
- case CS35L32_PWRCTL2:
- case CS35L32_CLK_CTL:
- case CS35L32_BATT_THRESHOLD:
- case CS35L32_VMON:
- case CS35L32_BST_CPCP_CTL:
- case CS35L32_IMON_SCALING:
- case CS35L32_AUDIO_LED_MNGR:
- case CS35L32_ADSP_CTL:
- case CS35L32_CLASSD_CTL:
- case CS35L32_PROTECT_CTL:
- case CS35L32_INT_MASK_1:
- case CS35L32_INT_MASK_2:
- case CS35L32_INT_MASK_3:
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
- case CS35L32_FLASH_MODE:
- case CS35L32_MOVIE_MODE:
- case CS35L32_FLASH_TIMER:
- case CS35L32_FLASH_INHIBIT:
+ case CS35L32_DEVID_AB ... CS35L32_AUDIO_LED_MNGR:
+ case CS35L32_ADSP_CTL ... CS35L32_FLASH_INHIBIT:
return true;
default:
return false;
@@ -111,15 +81,8 @@ static bool cs35l32_readable_register(struct device *dev, unsigned int reg)
static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_DEVID_AB:
- case CS35L32_DEVID_CD:
- case CS35L32_DEVID_E:
- case CS35L32_FAB_ID:
- case CS35L32_REV_ID:
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
+ case CS35L32_DEVID_AB ... CS35L32_REV_ID:
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
return true;
default:
return false;
@@ -129,10 +92,7 @@ static bool cs35l32_volatile_register(struct device *dev, unsigned int reg)
static bool cs35l32_precious_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS35L32_INT_STATUS_1:
- case CS35L32_INT_STATUS_2:
- case CS35L32_INT_STATUS_3:
- case CS35L32_LED_STATUS:
+ case CS35L32_INT_STATUS_1 ... CS35L32_LED_STATUS:
return true;
default:
return false;
@@ -190,16 +150,16 @@ static const struct snd_soc_dapm_route cs35l32_audio_map[] = {
static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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_update_bits(codec, CS35L32_ADSP_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS35L32_ADSP_CTL,
CS35L32_ADSP_MASTER_MASK, 0);
break;
default:
@@ -211,9 +171,9 @@ static int cs35l32_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int cs35l32_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- return snd_soc_update_bits(codec, CS35L32_PWRCTL2,
+ return snd_soc_component_update_bits(component, CS35L32_PWRCTL2,
CS35L32_SDOUT_3ST, tristate << 3);
}
@@ -234,11 +194,11 @@ static struct snd_soc_dai_driver cs35l32_dai[] = {
.formats = CS35L32_FORMATS,
},
.ops = &cs35l32_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
+static int cs35l32_component_set_sysclk(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir)
{
unsigned int val;
@@ -260,24 +220,25 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
return -EINVAL;
}
- return snd_soc_update_bits(codec, CS35L32_CLK_CTL,
+ return snd_soc_component_update_bits(component, CS35L32_CLK_CTL,
CS35L32_MCLK_DIV2_MASK | CS35L32_MCLK_RATIO_MASK, val);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs35l32 = {
- .set_sysclk = cs35l32_codec_set_sysclk,
-
- .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),
-
- .controls = cs35l32_snd_controls,
- .num_controls = ARRAY_SIZE(cs35l32_snd_controls),
+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_default cs35l32_monitor_patch[] = {
+static const struct reg_sequence cs35l32_monitor_patch[] = {
{ 0x00, 0x99 },
{ 0x48, 0x17 },
@@ -298,7 +259,10 @@ static const struct regmap_config cs35l32_regmap = {
.volatile_reg = cs35l32_volatile_register,
.readable_reg = cs35l32_readable_register,
.precious_reg = cs35l32_precious_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
@@ -310,7 +274,9 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
if (of_property_read_u32(np, "cirrus,sdout-share", &val) >= 0)
pdata->sdout_share = val;
- of_property_read_u32(np, "cirrus,boost-manager", &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:
@@ -318,13 +284,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
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;
}
- of_property_read_u32(np, "cirrus,sdout-datacfg", &val);
+ 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:
@@ -332,13 +300,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
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;
}
- of_property_read_u32(np, "cirrus,battery-threshold", &val);
+ 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:
@@ -346,13 +316,15 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
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;
}
- of_property_read_u32(np, "cirrus,battery-recovery", &val);
+ 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:
@@ -362,6 +334,7 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
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);
@@ -371,23 +344,17 @@ static int cs35l32_handle_of_data(struct i2c_client *i2c_client,
return 0;
}
-static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
- unsigned int devid = 0;
+ int ret, i, devid;
unsigned int reg;
-
- cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l32_private),
- GFP_KERNEL);
- if (!cs35l32) {
- dev_err(&i2c_client->dev, "could not allocate codec\n");
+ cs35l32 = devm_kzalloc(&i2c_client->dev, sizeof(*cs35l32), GFP_KERNEL);
+ if (!cs35l32)
return -ENOMEM;
- }
i2c_set_clientdata(i2c_client, cs35l32);
@@ -401,13 +368,11 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
if (pdata) {
cs35l32->pdata = *pdata;
} else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs35l32_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
+ 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);
@@ -437,50 +402,42 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
}
/* Reset the Device */
- cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev,
- "reset-gpios");
+ 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);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- cs35l32->reset_gpio = NULL;
- } else {
- ret = gpiod_direction_output(cs35l32->reset_gpio, 0);
- if (ret)
- return ret;
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+ goto err_supplies;
}
- /* initialize codec */
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
- ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ /* 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);
- return ret;
+ goto err_disable;
}
ret = regmap_read(cs35l32->regmap, CS35L32_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;
+ 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");
- return ret;
+ goto err_disable;
}
dev_info(&i2c_client->dev,
@@ -521,10 +478,10 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
CS35L32_PDN_AMP);
/* Clear MCLK Error Bit since we don't have the clock yet */
- ret = regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
+ regmap_read(cs35l32->regmap, CS35L32_INT_STATUS_1, &reg);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs35l32, cs35l32_dai,
+ 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;
@@ -532,25 +489,21 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client,
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 int cs35l32_i2c_remove(struct i2c_client *i2c_client)
+static void cs35l32_i2c_remove(struct i2c_client *i2c_client)
{
struct cs35l32_private *cs35l32 = i2c_get_clientdata(i2c_client);
- snd_soc_unregister_codec(&i2c_client->dev);
-
/* Hold down reset */
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
-
- return 0;
+ 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);
@@ -559,8 +512,7 @@ static int cs35l32_runtime_suspend(struct device *dev)
regcache_mark_dirty(cs35l32->regmap);
/* Hold down reset */
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
+ gpiod_set_value_cansleep(cs35l32->reset_gpio, 0);
/* remove power */
regulator_bulk_disable(ARRAY_SIZE(cs35l32->supplies),
@@ -583,19 +535,16 @@ static int cs35l32_runtime_resume(struct device *dev)
return ret;
}
- if (cs35l32->reset_gpio)
- gpiod_set_value_cansleep(cs35l32->reset_gpio, 1);
+ 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)
+ RUNTIME_PM_OPS(cs35l32_runtime_suspend, cs35l32_runtime_resume, NULL)
};
static const struct of_device_id cs35l32_of_match[] = {
@@ -606,7 +555,7 @@ MODULE_DEVICE_TABLE(of, cs35l32_of_match);
static const struct i2c_device_id cs35l32_id[] = {
- {"cs35l32", 0},
+ {"cs35l32"},
{}
};
@@ -615,8 +564,7 @@ MODULE_DEVICE_TABLE(i2c, cs35l32_id);
static struct i2c_driver cs35l32_i2c_driver = {
.driver = {
.name = "cs35l32",
- .owner = THIS_MODULE,
- .pm = &cs35l32_runtime_pm,
+ .pm = pm_ptr(&cs35l32_runtime_pm),
.of_match_table = cs35l32_of_match,
},
.id_table = cs35l32_id,
diff --git a/sound/soc/codecs/cs35l32.h b/sound/soc/codecs/cs35l32.h
index 31ab804a22bc..9471a30e9105 100644
--- a/sound/soc/codecs/cs35l32.h
+++ b/sound/soc/codecs/cs35l32.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs35l32.h -- CS35L32 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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 __CS35L32_H__
@@ -80,7 +76,7 @@ struct cs35l32_platform_data {
#define CS35L32_GAIN_MGR_MASK 0x08
#define CS35L32_ADSP_SHARE_MASK 0x08
#define CS35L32_ADSP_DATACFG_MASK 0x30
-#define CS35L32_SDOUT_3ST 0x80
+#define CS35L32_SDOUT_3ST 0x08
#define CS35L32_BATT_REC_MASK 0x0E
#define CS35L32_BATT_THRESH_MASK 0x30
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..c927592f90c9
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.c
@@ -0,0 +1,1287 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of.h>
+
+#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_CBP_CFP:
+ 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_CBC_CFC:
+ 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 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 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 = {
+ 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,
+ &current_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, &reg);
+ 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"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+ .driver = {
+ .name = "cs35l33",
+ .pm = pm_ptr(&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, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644
index 000000000000..fcb5e1723be6
--- /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 <paul.handrigan@cirrus.com>
+ */
+
+#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 000000000000..a5a8075598ff
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.c
@@ -0,0 +1,1217 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l34.c -- CS35l34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio/consumer.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l34.h>
+
+#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,
+ &reg);
+ 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,
+ &reg);
+ 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_CBP_CFP:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 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 = {
+ .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 const 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, &current1);
+
+ 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", 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, &reg);
+ 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 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 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 = {
+ 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"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l34_id);
+
+static struct i2c_driver cs35l34_i2c_driver = {
+ .driver = {
+ .name = "cs35l34",
+ .pm = pm_ptr(&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, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h
new file mode 100644
index 000000000000..97959e334f9b
--- /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 <Paul.Handrigan@cirrus.com>
+ */
+
+#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 000000000000..7a01b1d9fc9d
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1662 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/completion.h>
+
+#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,
+ &reg, 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 const 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, &current1);
+
+ /* 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, &reg);
+ 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"},
+ {}
+};
+
+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, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5e4509f41b32
--- /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 <brian.austin@cirrus.com>
+ */
+
+#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 000000000000..b60697ff7a50
--- /dev/null
+++ b/sound/soc/codecs/cs35l36.c
@@ -0,0 +1,1942 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l36.c -- CS35L36 ALSA SoC audio driver
+//
+// Copyright 2018 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/irq.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l36.h>
+#include <linux/completion.h>
+
+#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 const 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, &reg);
+
+ 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, &amp_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 struct snd_soc_dai_ops cs35l36_ops = {
+ .set_fmt = cs35l36_set_dai_fmt,
+ .hw_params = cs35l36_pcm_hw_params,
+ .set_sysclk = cs35l36_dai_set_sysclk,
+};
+
+#define CS35L36_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_384000)
+
+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 = CS35L36_RATES,
+ .formats = CS35L36_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L36_RATES,
+ .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 const 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, &reg_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, &reg_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"},
+ {}
+};
+
+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, <james.schulman@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h
new file mode 100644
index 000000000000..f6e38c633b93
--- /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 <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef __CS35L36_H__
+#define __CS35L36_H__
+
+#include <linux/regmap.h>
+
+#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 000000000000..34097996b784
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41-i2c.c -- CS35l41 I2C driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "cs35l41.h"
+
+static const struct i2c_device_id cs35l41_id_i2c[] = {
+ { "cs35l40" },
+ { "cs35l41" },
+ { "cs35l51" },
+ { "cs35l53" },
+ {}
+};
+
+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;
+
+ 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))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ 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 = pm_ptr(&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, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
new file mode 100644
index 000000000000..1702f26049d3
--- /dev/null
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -0,0 +1,1592 @@
+// 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 <david.rhodes@cirrus.com>
+// Author: Lucas Tanure <lucas.tanure@cirrus.com>
+
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/firmware/cirrus/wmfw.h>
+
+#include <sound/cs35l41.h>
+
+#define CS35L41_FIRMWARE_OLD_VERSION 0x001C00 /* v0.28.0 */
+
+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_SFT_RESET:
+ 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, const unsigned int *tx_slot,
+ unsigned int rx_num, const 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, struct cs_dsp *dsp)
+{
+ 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 (dsp->running && dsp->fw_id_version > CS35L41_FIRMWARE_OLD_VERSION)
+ 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 (sts == CSPL_MBOX_STS_ERROR || sts == CSPL_MBOX_STS_ERROR2) {
+ dev_err(dev, "CSPL Error Detected\n");
+ return -EINVAL;
+ }
+
+ 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, <david.rhodes@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure, Cirrus Logic Inc, <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41-spi.c b/sound/soc/codecs/cs35l41-spi.c
new file mode 100644
index 000000000000..f9b6bf7bea9c
--- /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 <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+
+#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;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ spi_set_drvdata(spi, cs35l41);
+ cs35l41->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l41->regmap))
+ return dev_err_probe(cs35l41->dev, PTR_ERR(cs35l41->regmap),
+ "Failed to allocate register map\n");
+
+ 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 = pm_ptr(&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, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
new file mode 100644
index 000000000000..173d7c59b725
--- /dev/null
+++ b/sound/soc/codecs/cs35l41.c
@@ -0,0 +1,1492 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l41.c -- CS35l41 ALSA SoC audio driver
+//
+// Copyright 2017-2021 Cirrus Logic, Inc.
+//
+// Author: David Rhodes <david.rhodes@cirrus.com>
+
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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_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);
+ 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);
+
+ 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,
+ const unsigned int *tx_slot,
+ unsigned int rx_n, const 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);
+
+ 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 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 = {
+ .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,
+};
+
+#define CS35L41_RATES ( \
+ SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | \
+ SNDRV_PCM_RATE_192000)
+
+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 = CS35L41_RATES,
+ .formats = CS35L41_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L41_RATES,
+ .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;
+ uint32_t dsp1rx5_src;
+ 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;
+ }
+
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VPMON;
+ break;
+ case CS35L41_EXT_BOOST:
+ case CS35L41_SHD_BOOST_PASS:
+ dsp1rx5_src = CS35L41_INPUT_SRC_VBSTMON;
+ break;
+ default:
+ dev_err(cs35l41->dev, "wm_halo_init failed - Invalid Boost Type: %d\n",
+ cs35l41->hw_cfg.bst_type);
+ goto err_dsp;
+ }
+
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX5_SRC, dsp1rx5_src);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write DSP1RX5_SRC: %d failed: %d\n", dsp1rx5_src, ret);
+ goto err_dsp;
+ }
+ ret = regmap_write(cs35l41->regmap, CS35L41_DSP1_RX6_SRC, CS35L41_INPUT_SRC_VBSTMON);
+ if (ret < 0) {
+ dev_err(cs35l41->dev, "Write CS35L41_INPUT_SRC_VBSTMON 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_get_system_name(struct cs35l41_private *cs35l41)
+{
+ struct acpi_device *adev = ACPI_COMPANION(cs35l41->dev);
+ const char *sub = NULL;
+ const char *tmp;
+ int ret = 0;
+
+ /* If there is no acpi_device, there is no ACPI for this system, skip checking ACPI */
+ if (adev) {
+ acpi_handle handle = acpi_device_handle(adev);
+
+ sub = acpi_get_subsystem_id(handle);
+ ret = PTR_ERR_OR_ZERO(sub);
+ if (ret) {
+ sub = NULL;
+ /* If no _SUB, fallback to _HID, otherwise fail */
+ if (ret == -ENODATA) {
+ tmp = acpi_device_hid(adev);
+ /* If dummy hid, return 0 and fallback to legacy firmware path */
+ if (!strcmp(tmp, "device")) {
+ ret = 0;
+ goto err;
+ }
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+ } else {
+ if (!device_property_read_string(cs35l41->dev, "cirrus,subsystem-id", &tmp)) {
+ sub = kstrdup(tmp, GFP_KERNEL);
+ if (!sub) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ }
+ }
+
+err:
+ if (sub) {
+ cs35l41->dsp.system_name = sub;
+ dev_info(cs35l41->dev, "Subsystem ID: %s\n", cs35l41->dsp.system_name);
+ } else
+ dev_warn(cs35l41->dev, "Subsystem ID not found\n");
+
+ return ret;
+}
+
+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)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to request core supplies\n");
+
+ ret = regulator_bulk_enable(CS35L41_NUM_SUPPLIES, cs35l41->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l41->dev, ret,
+ "Failed to enable core supplies\n");
+
+ /* 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_probe(cs35l41->dev, ret,
+ "Failed to get reset GPIO\n");
+ 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_probe(cs35l41->dev, ret,
+ "Failed waiting for OTP_BOOT_DONE\n");
+ 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, &regid);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Get Device ID failed\n");
+ goto err;
+ }
+
+ ret = regmap_read(cs35l41->regmap, CS35L41_REVID, &reg_revid);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Get Revision ID failed\n");
+ 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_probe(cs35l41->dev, ret, "OTP Unpack failed\n");
+ 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_probe(cs35l41->dev, ret, "Failed to request IRQ\n");
+ goto err;
+ }
+
+ ret = cs35l41_set_pdata(cs35l41);
+ if (ret < 0) {
+ dev_err_probe(cs35l41->dev, ret, "Set pdata failed\n");
+ goto err;
+ }
+
+ ret = cs35l41_get_system_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_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_probe(cs35l41->dev, ret, "Register codec failed\n");
+ 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 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 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 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 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 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 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;
+}
+
+EXPORT_GPL_DEV_PM_OPS(cs35l41_pm_ops) = {
+ RUNTIME_PM_OPS(cs35l41_runtime_suspend, cs35l41_runtime_resume, NULL)
+
+ SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend, cs35l41_sys_resume)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l41_sys_suspend_noirq, cs35l41_sys_resume_noirq)
+};
+
+MODULE_DESCRIPTION("ASoC CS35L41 driver");
+MODULE_AUTHOR("David Rhodes, Cirrus Logic Inc, <david.rhodes@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
new file mode 100644
index 000000000000..c85cbc1dd333
--- /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 <david.rhodes@cirrus.com>
+ */
+
+#ifndef __CS35L41_H__
+#define __CS35L41_H__
+
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/cs35l41.h>
+
+#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 000000000000..a09aa3b92ae1
--- /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 <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#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" },
+ {}
+};
+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, <james.schulman@cirrus.com>");
+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 000000000000..5f91472c3fd2
--- /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 <james.schulman@cirrus.com>
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#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, <james.schulman@cirrus.com>");
+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 000000000000..d2ecc7b3f619
--- /dev/null
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45-tables.c -- CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#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_AMP_GAIN, 0x00002300 },
+ { 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_HVLV_CONFIG:
+ case CS35L45_AMP_PCM_CONTROL:
+ case CS35L45_AMP_GAIN:
+ 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 },
+};
+
+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 000000000000..d4dcdf37bb70
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.c
@@ -0,0 +1,1515 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// cs35l45.c - CS35L45 ALSA SoC audio driver
+//
+// Copyright 2019-2022 Cirrus Logic, Inc.
+//
+// Author: James Schulman <james.schulman@cirrus.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/firmware.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 int cs35l45_activate_ctl(struct snd_soc_component *component,
+ const char *ctl_name, bool active)
+{
+ struct snd_card *card = component->card->snd_card;
+ struct snd_kcontrol *kcontrol;
+ struct snd_kcontrol_volatile *vd;
+ unsigned int index_offset;
+
+ kcontrol = snd_soc_component_get_kcontrol(component, ctl_name);
+ if (!kcontrol) {
+ dev_err(component->dev, "Can't find kcontrol %s\n", ctl_name);
+ return -EINVAL;
+ }
+
+ index_offset = snd_ctl_get_ioff(kcontrol, &kcontrol->id);
+ vd = &kcontrol->vd[index_offset];
+ if (active)
+ vd->access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ else
+ vd->access &= ~SNDRV_CTL_ELEM_ACCESS_WRITE;
+
+ snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &kcontrol->id);
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l45->amplifier_mode;
+
+ return 0;
+}
+
+static int cs35l45_amplifier_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+ unsigned int amp_state;
+ int ret;
+
+ if ((ucontrol->value.integer.value[0] == cs35l45->amplifier_mode) ||
+ (ucontrol->value.integer.value[0] > AMP_MODE_RCV))
+ return 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_BLOCK_ENABLES, &amp_state);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read AMP state: %d\n", ret);
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+ }
+
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+ snd_soc_component_disable_pin_unlocked(component, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+
+ if (ucontrol->value.integer.value[0] == AMP_MODE_SPK) {
+ regmap_clear_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_ENABLE << CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_HVLV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", true);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+
+ } else /* AMP_MODE_RCV */ {
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_RCV_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_BST_EN_MASK,
+ CS35L45_BST_DISABLE_FET_OFF <<
+ CS35L45_BST_EN_SHIFT);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_HVLV_CONFIG,
+ CS35L45_HVLV_MODE_MASK,
+ CS35L45_FORCE_LV_OPERATION <<
+ CS35L45_HVLV_MODE_SHIFT);
+
+ regmap_clear_bits(cs35l45->regmap,
+ CS35L45_BLOCK_ENABLES2,
+ CS35L45_AMP_DRE_EN_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_MASK,
+ CS35L45_AMP_GAIN_PCM_13DBV <<
+ CS35L45_AMP_GAIN_PCM_SHIFT);
+
+ ret = cs35l45_activate_ctl(component, "Analog PCM Volume", false);
+ if (ret < 0)
+ dev_err(cs35l45->dev,
+ "Unable to deactivate ctl (%d)\n", ret);
+ }
+
+ if (amp_state & CS35L45_AMP_EN_MASK)
+ regmap_set_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES,
+ CS35L45_AMP_EN_MASK);
+
+ snd_soc_component_enable_pin_unlocked(component, "SPK");
+ snd_soc_dapm_sync_unlocked(dapm);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ cs35l45->amplifier_mode = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+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_kcontrol_new amp_en_ctl =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 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("TEMPMON_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_SUPPLY("VMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("IMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_IMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("TEMPMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_TEMPMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BATTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BATTMON_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("VDD_BSTMON_EN", CS35L45_BLOCK_ENABLES, CS35L45_VDD_BSTMON_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("VMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("IMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("TEMPMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BATTMON", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("VDD_BSTMON", NULL, SND_SOC_NOPM, 0, 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_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0, &amp_en_ctl),
+
+ 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" },
+ { "TEMPMON", NULL, "TEMPMON_SRC" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_SRC" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_SRC" },
+
+ { "VMON", NULL, "VMON_EN" },
+ { "IMON", NULL, "IMON_EN" },
+ { "TEMPMON", NULL, "TEMPMON_EN" },
+ { "VDD_BATTMON", NULL, "VDD_BATTMON_EN" },
+ { "VDD_BSTMON", NULL, "VDD_BSTMON_EN" },
+
+ { "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", NULL, "VMON_EN"},
+ {"DSP1", NULL, "IMON_EN"},
+ {"DSP1", NULL, "VDD_BATTMON_EN"},
+ {"DSP1", NULL, "VDD_BSTMON_EN"},
+ {"DSP1", NULL, "TEMPMON_EN"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
+ CS35L45_DAC_MUX_ROUTE("DACPCM"),
+
+ { "AMP Enable", "Switch", "AMP" },
+ { "SPK", NULL, "AMP Enable"},
+};
+
+static const char * const amplifier_mode_texts[] = {"SPK", "RCV"};
+static SOC_ENUM_SINGLE_DECL(amplifier_mode_enum, SND_SOC_NOPM, 0,
+ amplifier_mode_texts);
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 1000, 300, 0);
+static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+
+static const struct snd_kcontrol_new cs35l45_controls[] = {
+ SOC_ENUM_EXT("Amplifier Mode", amplifier_mode_enum,
+ cs35l45_amplifier_mode_get, cs35l45_amplifier_mode_put),
+ SOC_SINGLE_TLV("Analog PCM Volume", CS35L45_AMP_GAIN,
+ CS35L45_AMP_GAIN_PCM_SHIFT,
+ CS35L45_AMP_GAIN_PCM_MASK >> CS35L45_AMP_GAIN_PCM_SHIFT,
+ 0, amp_gain_tlv),
+ /* 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);
+
+ if (i < 0 || i >= ARRAY_SIZE(cs35l45_irqs))
+ dev_err(cs35l45->dev, "Unspecified global error condition (%d) detected!\n", irq);
+ else
+ 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;
+
+ cs35l45->amplifier_mode = AMP_MODE_SPK;
+
+ 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_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, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
new file mode 100644
index 000000000000..7a790d2acac7
--- /dev/null
+++ b/sound/soc/codecs/cs35l45.h
@@ -0,0 +1,514 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * cs35l45.h - CS35L45 ALSA SoC audio driver
+ *
+ * Copyright 2019-2022 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef CS35L45_H
+#define CS35L45_H
+
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
+#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_HVLV_CONFIG 0x00006400
+#define CS35L45_LDPM_CONFIG 0x00006404
+#define CS35L45_AMP_PCM_CONTROL 0x00007000
+#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_AMP_GAIN 0x00007800
+#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_TEMPMON_EN_SHIFT 10
+#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_RCV_EN_SHIFT 2
+#define CS35L45_RCV_EN_MASK BIT(2)
+#define CS35L45_AMP_EN_SHIFT 0
+#define CS35L45_AMP_EN_MASK BIT(0)
+
+#define CS35L45_BST_DISABLE_FET_OFF 0x00
+#define CS35L45_BST_DISABLE_FET_ON 0x01
+#define CS35L45_BST_ENABLE 0x02
+
+/* BLOCK_ENABLES2 */
+#define CS35L45_ASP_EN_SHIFT 27
+#define CS35L45_AMP_DRE_EN_SHIFT 20
+#define CS35L45_AMP_DRE_EN_MASK BIT(20)
+#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)
+
+/* HVLV_CONFIG */
+#define CS35L45_FORCE_LV_OPERATION 0x01
+#define CS35L45_FORCE_HV_OPERATION 0x02
+#define CS35L45_HVLV_OPERATION 0x03
+#define CS35L45_HVLV_MODE_SHIFT 0
+#define CS35L45_HVLV_MODE_MASK GENMASK(1, 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
+
+/* AMP_GAIN_PCM */
+#define CS35L45_AMP_GAIN_PCM_10DBV 0x00
+#define CS35L45_AMP_GAIN_PCM_13DBV 0x01
+#define CS35L45_AMP_GAIN_PCM_16DBV 0x02
+#define CS35L45_AMP_GAIN_PCM_19DBV 0x03
+
+#define CS35L45_AMP_GAIN_PCM_SHIFT 8
+#define CS35L45_AMP_GAIN_PCM_MASK GENMASK(9, 8)
+
+/* 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,
+};
+
+enum amp_mode {
+ AMP_MODE_SPK = 0,
+ AMP_MODE_RCV = 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 amplifier_mode;
+ 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);
+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 000000000000..0492ddc4102d
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -0,0 +1,108 @@
+// 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 <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_i2c_probe(struct i2c_client *client)
+{
+ unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
+ struct cs35l56_private *cs35l56;
+ struct device *dev = &client->dev;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->base.can_hibernate = true;
+
+ i2c_set_clientdata(client, cs35l56);
+
+ switch (id) {
+ case 0x3556:
+ regmap_config = &cs35l56_regmap_i2c;
+ cs35l56->base.type = 0x56;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_i2c;
+ cs35l56->base.type = 0x63;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ 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", 0x3556 },
+ { "cs35l63", 0x3563 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
+ { "CSC355C", 0x3556 },
+ { "CSC356C", 0x3563 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
+#endif
+
+static struct i2c_driver cs35l56_i2c_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&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 <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
new file mode 100644
index 000000000000..42d24ac2977f
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -0,0 +1,606 @@
+// 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 <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "cs35l56.h"
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS35L56_SDW_ADDR_OFFSET 0x8000
+
+/* Cirrus bus bridge registers */
+#define CS35L56_SDW_MEM_ACCESS_STATUS 0xd0
+#define CS35L56_SDW_MEM_READ_DATA 0xd8
+
+#define CS35L56_SDW_LAST_LATE BIT(3)
+#define CS35L56_SDW_CMD_IN_PROGRESS BIT(2)
+#define CS35L56_SDW_RDATA_RDY BIT(0)
+
+#define CS35L56_LATE_READ_POLL_US 10
+#define CS35L56_LATE_READ_TIMEOUT_US 1000
+
+static int cs35l56_sdw_poll_mem_status(struct sdw_slave *peripheral,
+ unsigned int mask,
+ unsigned int match)
+{
+ int ret, val;
+
+ ret = read_poll_timeout(sdw_read_no_pm, val,
+ (val < 0) || ((val & mask) == match),
+ CS35L56_LATE_READ_POLL_US, CS35L56_LATE_READ_TIMEOUT_US,
+ false, peripheral, CS35L56_SDW_MEM_ACCESS_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (val < 0)
+ return val;
+
+ return 0;
+}
+
+static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
+ u8 *buf, size_t val_size)
+{
+ int ret, i;
+
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ for (i = 0; i < val_size; i += sizeof(u32)) {
+ /* Poll for bus bridge idle */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_CMD_IN_PROGRESS,
+ 0);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "!CMD_IN_PROGRESS fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Reading LSByte triggers read of register to holding buffer */
+ sdw_read_no_pm(peripheral, reg + i);
+
+ /* Wait for data available */
+ ret = cs35l56_sdw_poll_mem_status(peripheral,
+ CS35L56_SDW_RDATA_RDY,
+ CS35L56_SDW_RDATA_RDY);
+ if (ret < 0) {
+ dev_err(&peripheral->dev, "RDATA_RDY fail: %d\n", ret);
+ return ret;
+ }
+
+ /* Read data from buffer */
+ ret = sdw_nread_no_pm(peripheral, CS35L56_SDW_MEM_READ_DATA,
+ sizeof(u32), &buf[i]);
+ if (ret) {
+ dev_err(&peripheral->dev, "Late read @%#x failed: %d\n", reg + i, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)&buf[i]);
+ }
+
+ return 0;
+}
+
+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);
+
+ if (cs35l56_is_otp_register(reg))
+ return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size);
+
+ 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 int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0);
+ if (ret < 0)
+ return ret;
+
+ cs35l56->sdw_unique_id = ret & 0xf;
+
+ return 0;
+}
+
+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);
+
+ ret = cs35l56_sdw_get_unique_id(cs35l56);
+ if (ret)
+ goto out;
+
+ /* SoundWire UniqueId is used to index the calibration array */
+ if (cs35l56->base.cal_index < 0)
+ cs35l56->base.cal_index = cs35l56->sdw_unique_id;
+
+ 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_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->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 __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,
+#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;
+ const struct regmap_config *regmap_config;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->base.dev = dev;
+ cs35l56->sdw_peripheral = peripheral;
+ cs35l56->sdw_link_num = peripheral->bus->link_id;
+ INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+ dev_set_drvdata(dev, cs35l56);
+
+ switch ((unsigned int)id->driver_data) {
+ case 0x3556:
+ case 0x3557:
+ regmap_config = &cs35l56_regmap_sdw;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_sdw;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ cs35l56->base.type = ((unsigned int)id->driver_data) & 0xff;
+
+ cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ peripheral, regmap_config);
+ 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, 0x3556),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = pm_ptr(&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 <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
new file mode 100644
index 000000000000..9e6b9ca2f354
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -0,0 +1,1355 @@
+// 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 <linux/array_size.h>
+#include <linux/firmware/cirrus/wmfw.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <sound/cs-amp-lib.h>
+
+#include "cs35l56.h"
+
+static const struct reg_sequence cs35l56_patch[] = {
+ /*
+ * Firmware can change these to non-defaults to satisfy SDCA.
+ * Ensure that they are at known 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_MASK_18, 0x1f7df0ff },
+};
+
+static const struct reg_sequence cs35l56_patch_fw[] = {
+ /* 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 },
+};
+
+static const struct reg_sequence cs35l63_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
+int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
+ ARRAY_SIZE(cs35l56_patch));
+ if (ret)
+ return ret;
+
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
+ ARRAY_SIZE(cs35l56_patch_fw));
+ break;
+ case 0x63:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
+ ARRAY_SIZE(cs35l63_patch_fw));
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED");
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_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 const struct reg_default cs35l63_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { 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, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { 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_MASK_1, 0x8003ffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0x8c000fff },
+ { CS35L56_IRQ1_MASK_18, 0x0760f000 },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_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_OTP_MEM_53:
+ case CS35L56_OTP_MEM_54:
+ case CS35L56_OTP_MEM_55:
+ 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_DIE_STS1:
+ case CS35L56_DIE_STS2:
+ 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_common_volatile_reg(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;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_MAIN_RENDER_USER_MUTE:
+ case CS35L56_MAIN_RENDER_USER_VOLUME:
+ case CS35L56_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L63_MAIN_RENDER_USER_MUTE:
+ case CS35L63_MAIN_RENDER_USER_VOLUME:
+ case CS35L63_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static const struct cs35l56_fw_reg cs35l56_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l56_b2_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_B2_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_B2_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+static const struct cs35l56_fw_reg cs35l63_fw_reg = {
+ .fw_ver = CS35L63_DSP1_FW_VER,
+ .halo_state = CS35L63_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L63_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L63_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L63_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L63_MAIN_POSTURE_NUMBER,
+};
+
+static void cs35l56_set_fw_reg_table(struct cs35l56_base *cs35l56_base)
+{
+ switch (cs35l56_base->type) {
+ default:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ cs35l56_base->fw_reg = &cs35l56_fw_reg;
+ break;
+ default:
+ cs35l56_base->fw_reg = &cs35l56_b2_fw_reg;
+ break;
+ }
+ break;
+ case 0x63:
+ cs35l56_base->fw_reg = &cs35l63_fw_reg;
+ break;
+ }
+}
+
+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 val;
+
+ ret = cs35l56_mbox_send(cs35l56_base, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->pm_cur_stat,
+ 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 val = 0;
+ int read_ret, poll_ret;
+
+ /*
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read of the status register.
+ */
+ poll_ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ cs35l56_base->fw_reg->halo_state,
+ &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 {
+ u32 addr;
+ u32 value;
+} cs35l56_spi_system_reset_stages[] = {
+ { .addr = CS35L56_DSP_VIRTUAL1_MBOX_1, .value = CS35L56_MBOX_CMD_SYSTEM_RESET },
+ /* The next write is necessary to delimit the soft reset */
+ { .addr = CS35L56_DSP_MBOX_1_RAW, .value = CS35L56_MBOX_CMD_PING },
+};
+
+static void cs35l56_spi_issue_bus_locked_reset(struct cs35l56_base *cs35l56_base,
+ struct spi_device *spi)
+{
+ struct cs35l56_spi_payload *buf = cs35l56_base->spi_payload_buf;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = sizeof(*buf),
+ };
+ struct spi_message m;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_spi_system_reset_stages); i++) {
+ buf->addr = cpu_to_be32(cs35l56_spi_system_reset_stages[i].addr);
+ buf->value = cpu_to_be32(cs35l56_spi_system_reset_stages[i].value);
+ spi_message_init_with_transfers(&m, &t, 1);
+ ret = spi_sync_locked(spi, &m);
+ if (ret)
+ dev_warn(cs35l56_base->dev, "spi_sync failed: %d\n", ret);
+
+ usleep_range(CS35L56_SPI_RESET_TO_PORT_READY_US,
+ 2 * CS35L56_SPI_RESET_TO_PORT_READY_US);
+ }
+}
+
+static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
+{
+ struct spi_device *spi = to_spi_device(cs35l56_base->dev);
+ unsigned int val;
+ int read_ret, ret;
+
+ /*
+ * There must not be any other SPI bus activity while the amp is
+ * soft-resetting.
+ */
+ ret = spi_bus_lock(spi->controller);
+ if (ret) {
+ dev_warn(cs35l56_base->dev, "spi_bus_lock failed: %d\n", ret);
+ return;
+ }
+
+ cs35l56_spi_issue_bus_locked_reset(cs35l56_base, spi);
+ spi_bus_unlock(spi->controller);
+
+ /*
+ * Check firmware boot by testing for a response in MBOX_2.
+ * HALO_STATE cannot be trusted yet because the reset sequence
+ * can leave it with stale state. But MBOX is reset.
+ * The regmap must remain in cache-only until the chip has
+ * booted, so use a bypassed read.
+ */
+ ret = read_poll_timeout(regmap_read_bypassed, read_ret,
+ (val > 0) && (val < 0xffffffff),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US,
+ false,
+ cs35l56_base->regmap,
+ CS35L56_DSP_VIRTUAL1_MBOX_2,
+ &val);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "SPI reboot timed out(%d): MBOX2=%#x\n",
+ read_ret, val);
+ }
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l56_b2_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_B2_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static const struct reg_sequence cs35l63_system_reset_seq[] = {
+ REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0),
+ 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);
+
+ if (cs35l56_is_spi(cs35l56_base)) {
+ cs35l56_spi_system_reset(cs35l56_base);
+ return;
+ }
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ switch (cs35l56_base->rev) {
+ case 0xb0:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+ break;
+ default:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_b2_system_reset_seq,
+ ARRAY_SIZE(cs35l56_b2_system_reset_seq));
+ break;
+ }
+ break;
+ case 0x63:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l63_system_reset_seq,
+ ARRAY_SIZE(cs35l63_system_reset_seq));
+ break;
+ default:
+ break;
+ }
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (is_soundwire)
+ return;
+
+ cs35l56_wait_control_port_ready();
+
+ /* Leave in cache-only. This will be revoked when the chip has rebooted. */
+}
+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 < 1)
+ 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;
+
+ /*
+ * 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_base->fw_reg->prot_sts,
+ &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_ALLOW_AUTO_HIBERNATE),
+};
+
+static void cs35l56_issue_wake_event(struct cs35l56_base *cs35l56_base)
+{
+ unsigned int val;
+
+ /*
+ * Dummy transactions to trigger I2C/SPI auto-wake. Issue two
+ * transactions to meet the minimum required time from the rising edge
+ * to the last falling edge of wake.
+ *
+ * It uses bypassed read because we must wake the chip before
+ * disabling regmap cache-only.
+ */
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ usleep_range(CS35L56_WAKE_HOLD_TIME_US, 2 * CS35L56_WAKE_HOLD_TIME_US);
+
+ regmap_read_bypassed(cs35l56_base->regmap, CS35L56_IRQ1_STATUS, &val);
+
+ cs35l56_wait_control_port_ready();
+}
+
+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_base->fw_reg->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;
+ }
+
+ /*
+ * 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;
+
+ /* Must be done before releasing cache-only */
+ if (!is_soundwire)
+ cs35l56_issue_wake_event(cs35l56_base);
+
+out_sync:
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ 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:
+ regcache_cache_only(cs35l56_base->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ 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");
+
+struct cs35l56_pte {
+ u8 x;
+ u8 wafer_id;
+ u8 pte[2];
+ u8 lot[3];
+ u8 y;
+ u8 unused[3];
+ u8 dvs;
+} __packed;
+static_assert((sizeof(struct cs35l56_pte) % sizeof(u32)) == 0);
+
+static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+ struct cs35l56_pte pte;
+ u64 unique_id;
+ int ret;
+
+ ret = regmap_raw_read(cs35l56_base->regmap, CS35L56_OTP_MEM_53, &pte, sizeof(pte));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Failed to read OTP: %d\n", ret);
+ return ret;
+ }
+
+ unique_id = (u32)pte.lot[2] | ((u32)pte.lot[1] << 8) | ((u32)pte.lot[0] << 16);
+ unique_id <<= 32;
+ unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
+ ((u32)pte.dvs << 24);
+
+ *uid = unique_id;
+
+ return 0;
+}
+
+static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+ u32 tmp[2];
+ int ret;
+
+ ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret);
+ return ret;
+ }
+
+ *uid = tmp[1];
+ *uid <<= 32;
+ *uid |= tmp[0];
+
+ return 0;
+}
+
+/* Firmware calibration controls */
+const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
+ .alg_id = 0x9f210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_calibration_controls, "SND_SOC_CS35L56_SHARED");
+
+static const struct cirrus_amp_cal_controls cs35l63_calibration_controls = {
+ .alg_id = 0xbf210,
+ .mem_region = WMFW_ADSP2_YM,
+ .ambient = "CAL_AMBIENT",
+ .calr = "CAL_R",
+ .status = "CAL_STATUS",
+ .checksum = "CAL_CHECKSUM",
+};
+
+int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
+{
+ u64 silicon_uid = 0;
+ int ret;
+
+ /* Driver can't apply calibration to a secured part, so skip */
+ if (cs35l56_base->secured)
+ return 0;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ case 0x63:
+ ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", silicon_uid);
+
+ ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid,
+ cs35l56_base->cal_index,
+ &cs35l56_base->cal_data);
+
+ /* Only return an error status if probe should be aborted */
+ if ((ret == -ENOENT) || (ret == -EOVERFLOW))
+ return 0;
+
+ if (ret < 0)
+ return ret;
+
+ cs35l56_base->cal_data_valid = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_calibration, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
+ bool *fw_missing, unsigned int *fw_version)
+{
+ unsigned int prot_status;
+ int ret;
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts, &prot_status);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
+ return ret;
+ }
+
+ *fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
+
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->fw_ver, fw_version);
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED");
+
+void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ __be32 pid, sid, tid;
+ unsigned int alg_id;
+ int ret;
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ alg_id = 0x9f212;
+ break;
+ default:
+ alg_id = 0xbf212;
+ break;
+ }
+
+ scoped_guard(mutex, &cs_dsp->pwr_lock) {
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &pid, sizeof(pid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &sid, sizeof(sid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID",
+ WMFW_ADSP2_XM, alg_id),
+ 0, &tid, sizeof(tid));
+ }
+
+ if (ret)
+ dev_warn(cs35l56_base->dev, "Can't read tuning IDs");
+ else
+ dev_info(cs35l56_base->dev, "Tuning PID: %#x, SID: %#x, TID: %#x\n",
+ be32_to_cpu(pid), be32_to_cpu(sid), be32_to_cpu(tid));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_log_tuning, "SND_SOC_CS35L56_SHARED");
+
+int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured, fw_ver;
+ bool fw_missing;
+
+ /*
+ * When the system is not using a reset_gpio ensure the device is
+ * awake, otherwise the device has just been released from reset and
+ * the driver must wait for the control port to become usable.
+ */
+ if (!cs35l56_base->reset_gpio)
+ cs35l56_issue_wake_event(cs35l56_base);
+ else
+ cs35l56_wait_control_port_ready();
+
+ ret = regmap_read_bypassed(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);
+ cs35l56_set_fw_reg_table(cs35l56_base);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56_base);
+ if (ret)
+ return ret;
+
+ ret = regmap_read_bypassed(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 0x35A54:
+ case 0x35A56:
+ case 0x35A57:
+ cs35l56_base->calibration_controls = &cs35l56_calibration_controls;
+ break;
+ case 0x35A630:
+ cs35l56_base->calibration_controls = &cs35l63_calibration_controls;
+ devid = devid >> 4;
+ break;
+ default:
+ dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
+ return -ENODEV;
+ }
+
+ cs35l56_base->type = devid & 0xFF;
+
+ /* Silicon is now identified and booted so exit cache-only */
+ regcache_cache_only(cs35l56_base->regmap, false);
+
+ 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;
+ }
+
+ ret = cs35l56_read_prot_status(cs35l56_base, &fw_missing, &fw_ver);
+ if (ret)
+ return ret;
+
+ dev_info(cs35l56_base->dev, "Cirrus Logic CS35L%02X%s Rev %02X OTP%d fw:%d.%d.%d (patched=%u)\n",
+ cs35l56_base->type, cs35l56_base->secured ? "s" : "", cs35l56_base->rev, otpid,
+ fw_ver >> 16, (fw_ver >> 8) & 0xff, fw_ver & 0xff, !fw_missing);
+
+ /* 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");
+
+int cs35l56_get_speaker_id(struct cs35l56_base *cs35l56_base)
+{
+ struct gpio_descs *descs;
+ u32 speaker_id;
+ int i, ret;
+
+ /* Check for vendor-specific speaker ID method */
+ ret = cs_amp_get_vendor_spkid(cs35l56_base->dev);
+ if (ret >= 0) {
+ dev_dbg(cs35l56_base->dev, "Vendor Speaker ID = %d\n", ret);
+ return ret;
+ } else if (ret != -ENOENT) {
+ dev_err(cs35l56_base->dev, "Error getting vendor Speaker ID: %d\n", ret);
+ return ret;
+ }
+
+ /* Attempt to read the speaker type from a device property */
+ ret = device_property_read_u32(cs35l56_base->dev, "cirrus,speaker-id", &speaker_id);
+ if (!ret) {
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ return speaker_id;
+ }
+
+ /* Read the speaker type qualifier from the motherboard GPIOs */
+ descs = gpiod_get_array_optional(cs35l56_base->dev, "spk-id", GPIOD_IN);
+ if (!descs) {
+ return -ENOENT;
+ } else if (IS_ERR(descs)) {
+ ret = PTR_ERR(descs);
+ return dev_err_probe(cs35l56_base->dev, ret, "Failed to get spk-id-gpios\n");
+ }
+
+ speaker_id = 0;
+ for (i = 0; i < descs->ndescs; i++) {
+ ret = gpiod_get_value_cansleep(descs->desc[i]);
+ if (ret < 0) {
+ dev_err_probe(cs35l56_base->dev, ret, "Failed to read spk-id[%d]\n", i);
+ goto err;
+ }
+
+ speaker_id |= (ret << i);
+ }
+
+ dev_dbg(cs35l56_base->dev, "Speaker ID = %d\n", speaker_id);
+ ret = speaker_id;
+err:
+ gpiod_put_array(descs);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_speaker_id, "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");
+
+const 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");
+
+const 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");
+
+const 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");
+
+const struct regmap_config cs35l63_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_base = 0x8000,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_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 = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+
+MODULE_DESCRIPTION("ASoC CS35L56 Shared");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644
index 000000000000..9bc9b7c98390
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -0,0 +1,95 @@
+// 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 <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#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.type = 0x56;
+
+ 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;
+ cs35l56->base.can_hibernate = true;
+ ret = cs35l56_init_config_for_spi(&cs35l56->base, spi);
+ if (ret)
+ return ret;
+
+ 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 = pm_ptr(&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 <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
new file mode 100644
index 000000000000..2c1edbd636ef
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.c
@@ -0,0 +1,1564 @@
+// 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 <linux/acpi.h>
+#include <linux/array_size.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/cs-amp-lib.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 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 const struct snd_kcontrol_new cs35l63_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L63_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L63_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->base.fw_reg->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,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ },
+ {
+ .name = "cs35l56-sdw1c",
+ .id = 2,
+ .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 int cs35l56_write_cal(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ if (cs35l56->base.secured || !cs35l56->base.cal_data_valid)
+ return -ENODATA;
+
+ ret = wm_adsp_run(&cs35l56->dsp);
+ if (ret)
+ return ret;
+
+ ret = cs_amp_write_cal_coeffs(&cs35l56->dsp.cs_dsp,
+ cs35l56->base.calibration_controls,
+ &cs35l56->base.cal_data);
+
+ wm_adsp_stop(&cs35l56->dsp);
+
+ if (ret == 0)
+ dev_info(cs35l56->base.dev, "Calibration applied\n");
+
+ return ret;
+}
+
+static int cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56,
+ bool load_firmware)
+{
+ int ret;
+
+ /*
+ * Abort the first load if it didn't find the suffixed bins and
+ * we have an alternate fallback suffix.
+ */
+ cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix);
+
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) {
+ cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix;
+ cs35l56->fallback_fw_suffix = NULL;
+ cs35l56->dsp.bin_mandatory = false;
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ }
+
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, true);
+ if (ret)
+ return;
+
+ cs35l56_write_cal(cs35l56);
+
+ /* Always REINIT after applying patch or coefficients */
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+}
+
+static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing)
+{
+ int ret;
+
+ /*
+ * 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 = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing);
+ if (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->base.fw_reg->prot_sts,
+ CS35L56_FIRMWARE_MISSING);
+ cs35l56->base.fw_patched = true;
+
+ if (cs35l56_write_cal(cs35l56) == 0)
+ cs35l56_mbox_send(&cs35l56->base, CS35L56_MBOX_CMD_AUDIO_REINIT);
+
+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);
+ unsigned int firmware_version;
+ bool firmware_missing;
+ int ret;
+
+ if (!cs35l56->base.init_done)
+ return;
+
+ pm_runtime_get_sync(cs35l56->base.dev);
+
+ ret = cs35l56_read_prot_status(&cs35l56->base, &firmware_missing, &firmware_version);
+ if (ret)
+ goto err;
+
+ /* Populate fw file qualifier with the revision and security state */
+ kfree(cs35l56->dsp.fwf_name);
+ if (firmware_missing) {
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL, "%02x-dsp1", cs35l56->base.rev);
+ } else {
+ /* Firmware files must match the running firmware version */
+ cs35l56->dsp.fwf_name = kasprintf(GFP_KERNEL,
+ "%02x%s-%06x-dsp1",
+ cs35l56->base.rev,
+ cs35l56->base.secured ? "-s" : "",
+ firmware_version);
+ }
+
+ if (!cs35l56->dsp.fwf_name)
+ goto err;
+
+ dev_dbg(cs35l56->base.dev, "DSP fwf name: '%s' system name: '%s'\n",
+ cs35l56->dsp.fwf_name, cs35l56->dsp.system_name);
+
+ /*
+ * The firmware cannot be patched if it is already running from
+ * patch RAM. In this case the firmware files are versioned to
+ * match the running firmware version and will only contain
+ * tunings. We do not need to shutdown the firmware to apply
+ * tunings so can use the lower cost reinit sequence instead.
+ */
+ if (!firmware_missing)
+ cs35l56_reinit_patch(cs35l56);
+ else
+ cs35l56_patch(cs35l56, firmware_missing);
+
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp);
+err:
+ pm_runtime_put_autosuspend(cs35l56->base.dev);
+}
+
+static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+{
+ if (cs35l56->dsp.fwf_suffix)
+ return 0;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
+
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new name as an alternate.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ }
+
+ return 0;
+}
+
+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;
+ int ret;
+
+ 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)) {
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0) {
+ cs35l56->dsp.system_name = devm_kasprintf(cs35l56->base.dev,
+ GFP_KERNEL,
+ "%04x%04x-spkid%d",
+ vendor, device,
+ cs35l56->speaker_id);
+ } else {
+ 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->dsp.part = kasprintf(GFP_KERNEL, "cs35l%02x", cs35l56->base.type);
+ if (!cs35l56->dsp.part)
+ return -ENOMEM;
+
+ cs35l56->component = component;
+ ret = cs35l56_set_fw_suffix(cs35l56);
+ if (ret)
+ return ret;
+
+ 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);
+
+
+ switch (cs35l56->base.type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = snd_soc_add_component_controls(component, cs35l56_controls,
+ ARRAY_SIZE(cs35l56_controls));
+ break;
+ case 0x63:
+ ret = snd_soc_add_component_controls(component, cs35l63_controls,
+ ARRAY_SIZE(cs35l63_controls));
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
+
+ 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);
+
+ if (cs35l56->dsp.cs_dsp.booted)
+ wm_adsp_power_down(&cs35l56->dsp);
+
+ wm_adsp2_component_remove(&cs35l56->dsp, component);
+
+ kfree(cs35l56->dsp.part);
+ cs35l56->dsp.part = NULL;
+
+ kfree(cs35l56->dsp.fwf_name);
+ cs35l56->dsp.fwf_name = NULL;
+
+ cs35l56->component = NULL;
+}
+
+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),
+
+ .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_control_add_nop(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl)
+{
+ return 0;
+}
+
+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 is filled in later as it is based on the DEVID. In a
+ * SoundWire system that cannot be read until enumeration has occurred
+ * and the device has attached.
+ */
+ dsp->fw = 12;
+ dsp->wmfw_optional = true;
+
+ /*
+ * None of the firmware controls need to be exported so add a no-op
+ * callback that suppresses creating an ALSA control.
+ */
+ dsp->control_add = &cs35l56_control_add_nop;
+
+ 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;
+
+ /* Append a speaker qualifier if there is a speaker ID */
+ if (cs35l56->speaker_id >= 0)
+ cs35l56->dsp.system_name = devm_kasprintf(dev, GFP_KERNEL, "%s-spkid%d",
+ prop, cs35l56->speaker_id);
+ else
+ 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;
+}
+
+/*
+ * Some SoundWire laptops have a spk-id-gpios property but it points to
+ * the wrong ACPI Device node so can't be used to get the GPIO. Try to
+ * find the SDCA node containing the GpioIo resource and add a GPIO
+ * mapping to it.
+ */
+static const struct acpi_gpio_params cs35l56_af01_first_gpio = { 0, 0, false };
+static const struct acpi_gpio_mapping cs35l56_af01_spkid_gpios_mapping[] = {
+ { "spk-id-gpios", &cs35l56_af01_first_gpio, 1 },
+ { }
+};
+
+static void cs35l56_acpi_dev_release_driver_gpios(void *adev)
+{
+ acpi_dev_remove_driver_gpios(adev);
+}
+
+static int cs35l56_try_get_broken_sdca_spkid_gpio(struct cs35l56_private *cs35l56)
+{
+ struct fwnode_handle *af01_fwnode;
+ const union acpi_object *obj;
+ struct gpio_desc *desc;
+ int ret;
+
+ /* Find the SDCA node containing the GpioIo */
+ af01_fwnode = device_get_named_child_node(cs35l56->base.dev, "AF01");
+ if (!af01_fwnode) {
+ dev_dbg(cs35l56->base.dev, "No AF01 node\n");
+ return -ENOENT;
+ }
+
+ ret = acpi_dev_get_property(ACPI_COMPANION(cs35l56->base.dev),
+ "spk-id-gpios", ACPI_TYPE_PACKAGE, &obj);
+ if (ret) {
+ dev_dbg(cs35l56->base.dev, "Could not get spk-id-gpios package: %d\n", ret);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* The broken properties we can handle are a 4-element package (one GPIO) */
+ if (obj->package.count != 4) {
+ dev_warn(cs35l56->base.dev, "Unexpected spk-id element count %d\n",
+ obj->package.count);
+ fwnode_handle_put(af01_fwnode);
+ return -ENOENT;
+ }
+
+ /* Add a GPIO mapping if it doesn't already have one */
+ if (!fwnode_property_present(af01_fwnode, "spk-id-gpios")) {
+ struct acpi_device *adev = to_acpi_device_node(af01_fwnode);
+
+ /*
+ * Can't use devm_acpi_dev_add_driver_gpios() because the
+ * mapping isn't being added to the node pointed to by
+ * ACPI_COMPANION().
+ */
+ ret = acpi_dev_add_driver_gpios(adev, cs35l56_af01_spkid_gpios_mapping);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return dev_err_probe(cs35l56->base.dev, ret,
+ "Failed to add gpio mapping to AF01\n");
+ }
+
+ ret = devm_add_action_or_reset(cs35l56->base.dev,
+ cs35l56_acpi_dev_release_driver_gpios,
+ adev);
+ if (ret) {
+ fwnode_handle_put(af01_fwnode);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Added spk-id-gpios mapping to AF01\n");
+ }
+
+ desc = fwnode_gpiod_get_index(af01_fwnode, "spk-id", 0, GPIOD_IN, NULL);
+ if (IS_ERR(desc)) {
+ fwnode_handle_put(af01_fwnode);
+ ret = PTR_ERR(desc);
+ return dev_err_probe(cs35l56->base.dev, ret, "Get GPIO from AF01 failed\n");
+ }
+
+ ret = gpiod_get_value_cansleep(desc);
+ gpiod_put(desc);
+
+ if (ret < 0) {
+ fwnode_handle_put(af01_fwnode);
+ dev_err_probe(cs35l56->base.dev, ret, "Error reading spk-id GPIO\n");
+ return ret;
+ }
+
+ fwnode_handle_put(af01_fwnode);
+
+ dev_info(cs35l56->base.dev, "Got spk-id from AF01\n");
+
+ return ret;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ init_completion(&cs35l56->init_completion);
+ mutex_init(&cs35l56->base.irq_lock);
+ cs35l56->base.cal_index = -1;
+ cs35l56->speaker_id = -ENOENT;
+
+ 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_speaker_id(&cs35l56->base);
+ if (ACPI_COMPANION(cs35l56->base.dev) && cs35l56->sdw_peripheral && (ret == -ENOENT))
+ ret = cs35l56_try_get_broken_sdca_spkid_gpio(cs35l56);
+
+ if ((ret < 0) && (ret != -ENOENT))
+ goto err;
+
+ cs35l56->speaker_id = ret;
+
+ 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;
+
+ ret = cs35l56_set_patch(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ ret = cs35l56_get_calibration(&cs35l56->base);
+ if (ret)
+ return ret;
+
+ 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");
+
+ regcache_cache_only(cs35l56->base.regmap, false);
+ }
+
+ /* 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;
+
+ /* 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);
+
+ 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");
+
+#if IS_ENABLED(CONFIG_SND_SOC_CS35L56_I2C) || IS_ENABLED(CONFIG_SND_SOC_CS35L56_SPI)
+EXPORT_NS_GPL_DEV_PM_OPS(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE) = {
+ 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)
+};
+#endif
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS("SND_SOC_CS35L56_SHARED");
+MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
new file mode 100644
index 000000000000..40a1800a4585
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.h
@@ -0,0 +1,71 @@
+/* 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 <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+#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_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;
+ const char *fallback_fw_suffix;
+ struct work_struct sdw_irq_work;
+ bool sdw_irq_no_unmask;
+ bool soft_resetting;
+ bool sdw_attached;
+ struct completion init_completion;
+
+ int speaker_id;
+ u32 rx_mask;
+ u32 tx_mask;
+ u8 asp_slot_width;
+ u8 asp_slot_count;
+ bool tdm_mode;
+ bool sysclk_set;
+ u8 sdw_link_num;
+ u8 sdw_unique_id;
+};
+
+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/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c
new file mode 100644
index 000000000000..aa629ef53db4
--- /dev/null
+++ b/sound/soc/codecs/cs40l50-codec.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS40L50 Advanced Haptic Driver with waveform memory,
+// integrated DSP, and closed-loop algorithms
+//
+// Copyright 2024 Cirrus Logic, Inc.
+//
+// Author: James Ogletree <james.ogletree@cirrus.com>
+
+#include <linux/bitfield.h>
+#include <linux/mfd/cs40l50.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define CS40L50_REFCLK_INPUT 0x2C04
+#define CS40L50_ASP_CONTROL2 0x4808
+#define CS40L50_ASP_DATA_CONTROL5 0x4840
+
+/* PLL Config */
+#define CS40L50_PLL_REFCLK_BCLK 0x0
+#define CS40L50_PLL_REFCLK_MCLK 0x5
+#define CS40L50_PLL_REEFCLK_MCLK_CFG 0x00
+#define CS40L50_PLL_REFCLK_LOOP_MASK BIT(11)
+#define CS40L50_PLL_REFCLK_OPEN_LOOP 1
+#define CS40L50_PLL_REFCLK_CLOSED_LOOP 0
+#define CS40L50_PLL_REFCLK_LOOP_SHIFT 11
+#define CS40L50_PLL_REFCLK_FREQ_MASK GENMASK(10, 5)
+#define CS40L50_PLL_REFCLK_FREQ_SHIFT 5
+#define CS40L50_PLL_REFCLK_SEL_MASK GENMASK(2, 0)
+#define CS40L50_BCLK_RATIO_DEFAULT 32
+
+/* ASP Config */
+#define CS40L50_ASP_RX_WIDTH_SHIFT 24
+#define CS40L50_ASP_RX_WIDTH_MASK GENMASK(31, 24)
+#define CS40L50_ASP_RX_WL_MASK GENMASK(5, 0)
+#define CS40L50_ASP_FSYNC_INV_MASK BIT(2)
+#define CS40L50_ASP_BCLK_INV_MASK BIT(6)
+#define CS40L50_ASP_FMT_MASK GENMASK(10, 8)
+#define CS40L50_ASP_FMT_I2S 0x2
+
+struct cs40l50_pll_config {
+ unsigned int freq;
+ unsigned int cfg;
+};
+
+struct cs40l50_codec {
+ struct device *dev;
+ struct regmap *regmap;
+ unsigned int daifmt;
+ unsigned int bclk_ratio;
+ unsigned int rate;
+};
+
+static const struct cs40l50_pll_config cs40l50_pll_cfg[] = {
+ { 32768, 0x00 },
+ { 1536000, 0x1B },
+ { 3072000, 0x21 },
+ { 6144000, 0x28 },
+ { 9600000, 0x30 },
+ { 12288000, 0x33 },
+};
+
+static int cs40l50_get_clk_config(const unsigned int freq, unsigned int *cfg)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs40l50_pll_cfg); i++) {
+ if (cs40l50_pll_cfg[i].freq == freq) {
+ *cfg = cs40l50_pll_cfg[i].cfg;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int cs40l50_swap_ext_clk(struct cs40l50_codec *codec, const unsigned int clk_src)
+{
+ unsigned int cfg;
+ int ret;
+
+ switch (clk_src) {
+ case CS40L50_PLL_REFCLK_BCLK:
+ ret = cs40l50_get_clk_config(codec->bclk_ratio * codec->rate, &cfg);
+ if (ret)
+ return ret;
+ break;
+ case CS40L50_PLL_REFCLK_MCLK:
+ cfg = CS40L50_PLL_REEFCLK_MCLK_CFG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_OPEN_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_FREQ_MASK |
+ CS40L50_PLL_REFCLK_SEL_MASK,
+ (cfg << CS40L50_PLL_REFCLK_FREQ_SHIFT) | clk_src);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(codec->regmap, CS40L50_REFCLK_INPUT,
+ CS40L50_PLL_REFCLK_LOOP_MASK,
+ CS40L50_PLL_REFCLK_CLOSED_LOOP <<
+ CS40L50_PLL_REFCLK_LOOP_SHIFT);
+}
+
+static int cs40l50_clk_en(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 cs40l50_codec *codec = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_STOP_PLAYBACK);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_dsp_write(codec->dev, codec->regmap, CS40L50_START_I2S);
+ if (ret)
+ return ret;
+
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_BCLK);
+ if (ret)
+ return ret;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = cs40l50_swap_ext_clk(codec, CS40L50_PLL_REFCLK_MCLK);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs40l50_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY_S("ASP PLL", 0, SND_SOC_NOPM, 0, 0, cs40l50_clk_en,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route cs40l50_dapm_routes[] = {
+ { "ASP Playback", NULL, "ASP PLL" },
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+
+ { "OUT", NULL, "ASPRX1" },
+ { "OUT", NULL, "ASPRX2" },
+};
+
+static int cs40l50_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(codec_dai->component);
+
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
+ return -EINVAL;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ codec->daifmt = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ codec->daifmt = CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ codec->daifmt = CS40L50_ASP_FSYNC_INV_MASK | CS40L50_ASP_BCLK_INV_MASK;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codec->daifmt |= FIELD_PREP(CS40L50_ASP_FMT_MASK, CS40L50_ASP_FMT_I2S);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs40l50_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+ unsigned int asp_rx_wl = params_width(params);
+ int ret;
+
+ codec->rate = params_rate(params);
+
+ ret = regmap_update_bits(codec->regmap, CS40L50_ASP_DATA_CONTROL5,
+ CS40L50_ASP_RX_WL_MASK, asp_rx_wl);
+ if (ret)
+ return ret;
+
+ codec->daifmt |= (asp_rx_wl << CS40L50_ASP_RX_WIDTH_SHIFT);
+
+ return regmap_update_bits(codec->regmap, CS40L50_ASP_CONTROL2,
+ CS40L50_ASP_FSYNC_INV_MASK |
+ CS40L50_ASP_BCLK_INV_MASK |
+ CS40L50_ASP_FMT_MASK |
+ CS40L50_ASP_RX_WIDTH_MASK, codec->daifmt);
+}
+
+static int cs40l50_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(dai->component);
+
+ codec->bclk_ratio = ratio;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs40l50_dai_ops = {
+ .set_fmt = cs40l50_set_dai_fmt,
+ .set_bclk_ratio = cs40l50_set_dai_bclk_ratio,
+ .hw_params = cs40l50_hw_params,
+};
+
+static struct snd_soc_dai_driver cs40l50_dai[] = {
+ {
+ .name = "cs40l50-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ },
+ .ops = &cs40l50_dai_ops,
+ },
+};
+
+static int cs40l50_codec_probe(struct snd_soc_component *component)
+{
+ struct cs40l50_codec *codec = snd_soc_component_get_drvdata(component);
+
+ codec->bclk_ratio = CS40L50_BCLK_RATIO_DEFAULT;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_cs40l50 = {
+ .probe = cs40l50_codec_probe,
+ .dapm_widgets = cs40l50_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs40l50_dapm_widgets),
+ .dapm_routes = cs40l50_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs40l50_dapm_routes),
+};
+
+static int cs40l50_codec_driver_probe(struct platform_device *pdev)
+{
+ struct cs40l50 *cs40l50 = dev_get_drvdata(pdev->dev.parent);
+ struct cs40l50_codec *codec;
+
+ codec = devm_kzalloc(&pdev->dev, sizeof(*codec), GFP_KERNEL);
+ if (!codec)
+ return -ENOMEM;
+
+ codec->regmap = cs40l50->regmap;
+ codec->dev = &pdev->dev;
+
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cs40l50,
+ cs40l50_dai, ARRAY_SIZE(cs40l50_dai));
+}
+
+static const struct platform_device_id cs40l50_id[] = {
+ { "cs40l50-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs40l50_id);
+
+static struct platform_driver cs40l50_codec_driver = {
+ .probe = cs40l50_codec_driver_probe,
+ .id_table = cs40l50_id,
+ .driver = {
+ .name = "cs40l50-codec",
+ },
+};
+module_platform_driver(cs40l50_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS40L50 driver");
+MODULE_AUTHOR("James Ogletree <james.ogletree@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
new file mode 100644
index 000000000000..dda7f5b4f2fb
--- /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 <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/jiffies.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/workqueue.h>
+
+#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_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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 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 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 = {
+ 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 = pm_ptr(&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 <tanureal@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/cs4234.h b/sound/soc/codecs/cs4234.h
new file mode 100644
index 000000000000..76a75afc198d
--- /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
index ce6086835ebd..3f759c13d6d1 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs4265.c -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
- *
- * 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.
- *
*/
#include <linux/module.h>
@@ -60,23 +56,7 @@ static const struct reg_default cs4265_reg_defaults[] = {
static bool cs4265_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS4265_PWRCTL:
- case CS4265_DAC_CTL:
- case CS4265_ADC_CTL:
- case CS4265_MCLK_FREQ:
- case CS4265_SIG_SEL:
- case CS4265_CHB_PGA_CTL:
- case CS4265_CHA_PGA_CTL:
- case CS4265_ADC_CTL2:
- case CS4265_DAC_CHA_VOL:
- case CS4265_DAC_CHB_VOL:
- case CS4265_DAC_CTL2:
- case CS4265_SPDIF_CTL1:
- case CS4265_SPDIF_CTL2:
- case CS4265_INT_MASK:
- case CS4265_STATUS_MODE_MSB:
- case CS4265_STATUS_MODE_LSB:
- case CS4265_CHIP_ID:
+ case CS4265_CHIP_ID ... CS4265_MAX_REGISTER:
return true;
default:
return false;
@@ -173,8 +153,7 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = {
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", 0,
- 1, 1, 0),
+ 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),
};
@@ -206,7 +185,7 @@ static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
&loopback_ctl),
- SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_SWITCH("SPDIF", CS4265_SPDIF_CTL2, 5, 1,
&spdif_switch),
SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
&dac_switch),
@@ -237,10 +216,11 @@ static const struct snd_soc_dapm_route cs4265_audio_map[] = {
{"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 Mux", "MIC", "MICL"},
- {"ADC Mux", "MIC", "MICR"},
{"ADC", NULL, "ADC Mux"},
{"DOUT", NULL, "ADC"},
{"DAI1 Capture", NULL, "DOUT"},
@@ -338,12 +318,12 @@ static int cs4265_get_clk_index(int mclk, int rate)
static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid clk_id %d\n", clk_id);
+ dev_err(component->dev, "Invalid clk_id %d\n", clk_id);
return -EINVAL;
}
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
@@ -353,24 +333,24 @@ static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
}
}
cs4265->sysclk = 0;
- dev_err(codec->dev, "Invalid freq parameter %d\n", freq);
+ 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_codec *codec = codec_dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
CS4265_ADC_MASTER);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_MASTER,
0);
break;
@@ -397,22 +377,22 @@ static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
-static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs4265_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
CS4265_DAC_CTL_MUTE);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
CS4265_SPDIF_CTL2_MUTE);
} else {
- snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_MUTE,
0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
CS4265_SPDIF_CTL2_MUTE,
0);
}
@@ -423,8 +403,8 @@ 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_codec *codec = dai->codec;
- struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ 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 &&
@@ -434,46 +414,46 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
if (index >= 0) {
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_FM, clk_map_table[index].fm_mode << 6);
- snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
+ snd_soc_component_update_bits(component, CS4265_MCLK_FREQ,
CS4265_MCLK_FREQ_MASK,
clk_map_table[index].mclkdiv << 4);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ 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_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, (1 << 4));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ 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_update_bits(codec, CS4265_DAC_CTL,
- CS4265_DAC_CTL_DIF, (1 << 5));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ 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_update_bits(codec, CS4265_DAC_CTL,
- CS4265_DAC_CTL_DIF, (3 << 5));
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ 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_update_bits(codec, CS4265_DAC_CTL,
+ snd_soc_component_update_bits(component, CS4265_DAC_CTL,
CS4265_DAC_CTL_DIF, 0);
- snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ snd_soc_component_update_bits(component, CS4265_ADC_CTL,
CS4265_ADC_DIF, 0);
- snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
- CS4265_SPDIF_CTL2_DIF, (1 << 6));
+ snd_soc_component_update_bits(component, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, 0);
break;
default:
@@ -482,28 +462,27 @@ static int cs4265_pcm_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int cs4265_set_bias_level(struct snd_soc_codec *codec,
+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_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN, 0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS4265_PWRCTL,
+ snd_soc_component_update_bits(component, CS4265_PWRCTL,
CS4265_PWRCTL_PDN,
CS4265_PWRCTL_PDN);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -513,13 +492,15 @@ static int cs4265_set_bias_level(struct snd_soc_codec *codec,
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_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,
- .digital_mute = cs4265_digital_mute,
+ .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[] = {
@@ -561,16 +542,17 @@ static struct snd_soc_dai_driver cs4265_dai[] = {
},
};
-static const struct snd_soc_codec_driver soc_codec_cs4265 = {
- .set_bias_level = cs4265_set_bias_level,
-
- .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),
-
- .controls = cs4265_snd_controls,
- .num_controls = ARRAY_SIZE(cs4265_snd_controls),
+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 = {
@@ -582,14 +564,13 @@ static const struct regmap_config cs4265_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
.readable_reg = cs4265_readable_register,
.volatile_reg = cs4265_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs4265_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+static int cs4265_i2c_probe(struct i2c_client *i2c_client)
{
struct cs4265_private *cs4265;
- int ret = 0;
+ int ret;
unsigned int devid = 0;
unsigned int reg;
@@ -605,32 +586,30 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev,
- "reset-gpios");
- if (IS_ERR(cs4265->reset_gpio)) {
- ret = PTR_ERR(cs4265->reset_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- 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);
- cs4265->reset_gpio = NULL;
- } else {
- ret = gpiod_direction_output(cs4265->reset_gpio, 0);
- if (ret)
- return ret;
+ 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, &reg);
+ 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 Device ID (%X). Expected %X\n",
- devid, CS4265_CHIP_ID);
+ "CS4265 Part Number ID: 0x%x Expected: 0x%x\n",
+ devid >> 4, CS4265_CHIP_ID_VAL >> 4);
return ret;
}
dev_info(&i2c_client->dev,
@@ -639,16 +618,17 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_cs4265, cs4265_dai,
+ return devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_cs4265, cs4265_dai,
ARRAY_SIZE(cs4265_dai));
- return ret;
}
-static int cs4265_i2c_remove(struct i2c_client *client)
+static void cs4265_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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[] = {
@@ -658,7 +638,7 @@ static const struct of_device_id cs4265_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4265_of_match);
static const struct i2c_device_id cs4265_id[] = {
- { "cs4265", 0 },
+ { "cs4265" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4265_id);
@@ -666,7 +646,6 @@ MODULE_DEVICE_TABLE(i2c, cs4265_id);
static struct i2c_driver cs4265_i2c_driver = {
.driver = {
.name = "cs4265",
- .owner = THIS_MODULE,
.of_match_table = cs4265_of_match,
},
.id_table = cs4265_id,
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h
index 0a80a8dcec67..8bc28c2bf99e 100644
--- a/sound/soc/codecs/cs4265.h
+++ b/sound/soc/codecs/cs4265.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs4265.h -- CS4265 ALSA SoC audio driver
*
* Copyright 2014 Cirrus Logic, Inc.
*
* Author: Paul Handrigan <paul.handrigan@cirrus.com>
- *
- * 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 __CS4265_H__
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e6d4ff9fd992..9f9dc8b017a3 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -21,6 +21,7 @@
* - Power management is supported
*/
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,21 +30,11 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
-/*
- * The codec isn't really big-endian or little-endian, since the I2S
- * interface requires data to be sent serially with the MSbit first.
- * However, to support BE and LE I2S devices, we specify both here. That
- * way, ALSA will always match the bit patterns.
- */
-#define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
- SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#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 */
@@ -137,6 +128,9 @@ struct cs4270_private {
/* 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[] = {
@@ -148,11 +142,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
@@ -219,7 +213,7 @@ 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 1;
+ return true;
return reg == CS4270_CHIPID;
}
@@ -254,8 +248,8 @@ static bool cs4270_reg_is_volatile(struct device *dev, unsigned int reg)
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
cs4270->mclk = freq;
return 0;
@@ -277,8 +271,8 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -287,21 +281,21 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid dai format\n");
+ 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:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs4270->slave_mode = 1;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs4270->slave_mode = 0;
break;
default:
/* all other modes are unsupported by the hardware */
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -326,8 +320,8 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -346,13 +340,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
/* Set the sample rate */
- reg = snd_soc_read(codec, CS4270_MODE);
+ reg = snd_soc_component_read(component, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
reg |= cs4270_mode_ratios[i].mclk;
@@ -361,15 +355,15 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
else
reg |= cs4270_mode_ratios[i].speed_mode;
- ret = snd_soc_write(codec, CS4270_MODE, reg);
+ ret = snd_soc_component_write(component, CS4270_MODE, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
/* Set the DAI format */
- reg = snd_soc_read(codec, CS4270_FORMAT);
+ reg = snd_soc_component_read(component, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
switch (cs4270->mode) {
@@ -380,13 +374,13 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
- dev_err(codec->dev, "unknown dai format\n");
+ dev_err(component->dev, "unknown dai format\n");
return -EINVAL;
}
- ret = snd_soc_write(codec, CS4270_FORMAT, reg);
+ ret = snd_soc_component_write(component, CS4270_FORMAT, reg);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -397,19 +391,20 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
* 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)
+static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg6;
- reg6 = snd_soc_read(codec, CS4270_MUTE);
+ reg6 = snd_soc_component_read(component, CS4270_MUTE);
if (mute)
reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
@@ -418,7 +413,7 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
reg6 |= cs4270->manual_mute;
}
- return snd_soc_write(codec, CS4270_MUTE, reg6);
+ return snd_soc_component_write(component, CS4270_MUTE, reg6);
}
/**
@@ -438,8 +433,8 @@ static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -468,7 +463,8 @@ 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,
- .digital_mute = cs4270_dai_mute,
+ .mute_stream = cs4270_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs4270_dai = {
@@ -496,14 +492,14 @@ static struct snd_soc_dai_driver cs4270_dai = {
/**
* cs4270_probe - ASoC probe function
- * @pdev: platform device
+ * @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_codec *codec)
+static int cs4270_probe(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int ret;
/* Disable auto-mute. This feature appears to be buggy. In some
@@ -511,9 +507,9 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
- ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
+ ret = snd_soc_component_update_bits(component, CS4270_MUTE, CS4270_MUTE_AUTO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -522,10 +518,10 @@ static int cs4270_probe(struct snd_soc_codec *codec)
* playback has started. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
- ret = snd_soc_update_bits(codec, CS4270_TRANS,
+ ret = snd_soc_component_update_bits(component, CS4270_TRANS,
CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0);
if (ret < 0) {
- dev_err(codec->dev, "i2c write failed\n");
+ dev_err(component->dev, "i2c write failed\n");
return ret;
}
@@ -537,17 +533,15 @@ static int cs4270_probe(struct snd_soc_codec *codec)
/**
* cs4270_remove - ASoC remove function
- * @pdev: platform device
+ * @component: ASoC component
*
* This function is the counterpart to cs4270_probe().
*/
-static int cs4270_remove(struct snd_soc_codec *codec)
+static void cs4270_remove(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
-
- return 0;
};
#ifdef CONFIG_PM
@@ -561,16 +555,16 @@ static int cs4270_remove(struct snd_soc_codec *codec)
* and all registers are written back to the hardware when resuming.
*/
-static int cs4270_soc_suspend(struct snd_soc_codec *codec)
+static int cs4270_soc_suspend(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
- reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ reg = snd_soc_component_read(component, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
if (reg < 0)
return reg;
- ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+ ret = snd_soc_component_write(component, CS4270_PWRCTL, reg);
if (ret < 0)
return ret;
@@ -580,9 +574,9 @@ static int cs4270_soc_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int cs4270_soc_resume(struct snd_soc_codec *codec)
+static int cs4270_soc_resume(struct snd_soc_component *component)
{
- struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec);
+ struct cs4270_private *cs4270 = snd_soc_component_get_drvdata(component);
int reg, ret;
ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
@@ -598,10 +592,10 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
regcache_sync(cs4270->regmap);
/* ... then disable the power-down bits */
- reg = snd_soc_read(codec, CS4270_PWRCTL);
+ reg = snd_soc_component_read(component, CS4270_PWRCTL);
reg &= ~CS4270_PWRCTL_PDN_ALL;
- return snd_soc_write(codec, CS4270_PWRCTL, reg);
+ return snd_soc_component_write(component, CS4270_PWRCTL, reg);
}
#else
#define cs4270_soc_suspend NULL
@@ -611,18 +605,20 @@ static int cs4270_soc_resume(struct snd_soc_codec *codec)
/*
* ASoC codec driver structure
*/
-static const struct snd_soc_codec_driver soc_codec_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),
+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,
};
/*
@@ -640,24 +636,36 @@ static const struct regmap_config cs4270_regmap = {
.max_register = CS4270_LASTREG,
.reg_defaults = cs4270_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4270_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .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
- * @id: the I2C device ID (ignored)
*
* 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,
- const struct i2c_device_id *id)
+static int cs4270_i2c_probe(struct i2c_client *i2c_client)
{
- struct device_node *np = i2c_client->dev.of_node;
struct cs4270_private *cs4270;
unsigned int val;
int ret, i;
@@ -677,21 +685,22 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
if (ret < 0)
return ret;
- /* See if we have a way to bring the codec out of reset */
- if (np) {
- enum of_gpio_flags flags;
- int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
-
- if (gpio_is_valid(gpio)) {
- ret = devm_gpio_request_one(&i2c_client->dev, gpio,
- flags & OF_GPIO_ACTIVE_LOW ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- "cs4270 reset");
- 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);
@@ -716,28 +725,16 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
i2c_set_clientdata(i2c_client, cs4270);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_device_cs4270, &cs4270_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_device_cs4270, &cs4270_dai, 1);
return ret;
}
-/**
- * cs4270_i2c_remove - remove an I2C device
- * @i2c_client: the I2C client object
- *
- * This function is the counterpart to cs4270_i2c_probe().
- */
-static int cs4270_i2c_remove(struct i2c_client *i2c_client)
-{
- snd_soc_unregister_codec(&i2c_client->dev);
- return 0;
-}
-
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static const struct i2c_device_id cs4270_id[] = {
- {"cs4270", 0},
+ {"cs4270"},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
@@ -751,7 +748,6 @@ MODULE_DEVICE_TABLE(i2c, cs4270_id);
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "cs4270",
- .owner = THIS_MODULE,
.of_match_table = cs4270_of_match,
},
.id_table = cs4270_id,
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index b264da030340..1d210b969173 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 I2C audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -20,27 +11,19 @@
#include <sound/soc.h>
#include "cs4271.h"
-static int cs4271_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int cs4271_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
config = cs4271_regmap_config;
config.reg_bits = 8;
- config.val_bits = 8;
return cs4271_probe(&client->dev,
devm_regmap_init_i2c(client, &config));
}
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id cs4271_i2c_id[] = {
- { "cs4271", 0 },
+ { "cs4271" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
@@ -48,11 +31,9 @@ MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
static struct i2c_driver cs4271_i2c_driver = {
.driver = {
.name = "cs4271",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_i2c_probe,
- .remove = cs4271_i2c_remove,
.id_table = cs4271_i2c_id,
};
module_i2c_driver(cs4271_i2c_driver);
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
index acd49d86e706..4feb80436bd9 100644
--- a/sound/soc/codecs/cs4271-spi.c
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 SPI audio driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
@@ -26,27 +17,18 @@ static int cs4271_spi_probe(struct spi_device *spi)
config = cs4271_regmap_config;
config.reg_bits = 16;
- config.val_bits = 8;
config.read_flag_mask = 0x21;
config.write_flag_mask = 0x20;
return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
}
-static int cs4271_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver cs4271_spi_driver = {
.driver = {
.name = "cs4271",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(cs4271_dt_ids),
},
.probe = cs4271_spi_probe,
- .remove = cs4271_spi_remove,
};
module_spi_driver(cs4271_spi_driver);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 79a4efcb894c..6a3cca3d26c7 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CS4271 ASoC codec driver
*
* Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* 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.
@@ -22,10 +13,9 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/tlv.h>
@@ -157,6 +147,10 @@ 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;
@@ -165,11 +159,10 @@ struct cs4271_private {
/* 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;
+ struct gpio_desc *reset;
/* 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[] = {
@@ -200,8 +193,8 @@ static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
cs4271->mclk = freq;
return 0;
@@ -210,21 +203,21 @@ static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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 = 0;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ cs4271->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- cs4271->master = 1;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs4271->master = true;
val |= CS4271_MODE1_MASTER;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -244,7 +237,7 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
return ret;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -257,9 +250,9 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int cs4271_deemph[] = {0, 44100, 48000, 32000};
-static int cs4271_set_deemph(struct snd_soc_codec *codec)
+static int cs4271_set_deemph(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
int val = CS4271_DACCTL_DEM_DIS;
@@ -283,21 +276,21 @@ static int cs4271_set_deemph(struct snd_soc_codec *codec)
static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = cs4271->deemph;
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
- cs4271->deemph = ucontrol->value.enumerated.item[0];
- return cs4271_set_deemph(codec);
+ cs4271->deemph = ucontrol->value.integer.value[0];
+ return cs4271_set_deemph(component);
}
struct cs4271_clk_cfg {
@@ -337,14 +330,14 @@ static struct cs4271_clk_cfg cs4271_clk_tab[] = {
{0, CS4271_MODE1_MODE_4X, 256, CS4271_MODE1_DIV_2},
};
-#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+#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_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
int i, ret;
unsigned int ratio, val;
@@ -359,9 +352,9 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
*/
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- !dai->capture_active) ||
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_CAPTURE)) ||
(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
- !dai->playback_active)) {
+ !snd_soc_dai_stream_active(dai, SNDRV_PCM_STREAM_PLAYBACK))) {
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN,
CS4271_MODE2_PDN);
@@ -386,14 +379,14 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
val = CS4271_MODE1_MODE_4X;
ratio = cs4271->mclk / cs4271->rate;
- for (i = 0; i < CS4171_NR_RATIOS; i++)
+ 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 == CS4171_NR_RATIOS) {
- dev_err(codec->dev, "Invalid sample rate\n");
+ if (i == CS4271_NR_RATIOS) {
+ dev_err(component->dev, "Invalid sample rate\n");
return -EINVAL;
}
@@ -404,13 +397,13 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return cs4271_set_deemph(codec);
+ return cs4271_set_deemph(component);
}
static int cs4271_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -484,14 +477,26 @@ static struct snd_soc_dai_driver cs4271_dai = {
.formats = CS4271_PCM_FORMATS,
},
.ops = &cs4271_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
+static int cs4271_reset(struct snd_soc_component *component)
+{
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
+
+ gpiod_direction_output(cs4271->reset, 1);
+ mdelay(1);
+ gpiod_set_value(cs4271->reset, 0);
+ mdelay(1);
+
+ return 0;
+}
+
#ifdef CONFIG_PM
-static int cs4271_soc_suspend(struct snd_soc_codec *codec)
+static int cs4271_soc_suspend(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ struct cs4271_private *cs4271 = snd_soc_component_get_drvdata(component);
/* Set power-down bit */
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
@@ -499,13 +504,26 @@ static int cs4271_soc_suspend(struct snd_soc_codec *codec)
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_codec *codec)
+static int cs4271_soc_resume(struct snd_soc_component *component)
{
int ret;
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -534,38 +552,36 @@ MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
EXPORT_SYMBOL_GPL(cs4271_dt_ids);
#endif
-static int cs4271_codec_probe(struct snd_soc_codec *codec)
+static int cs4271_component_probe(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+ 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;
+ bool amutec_eq_bmutec;
-#ifdef CONFIG_OF
- if (of_match_device(cs4271_dt_ids, codec->dev)) {
- if (of_get_property(codec->dev->of_node,
- "cirrus,amutec-eq-bmutec", NULL))
- amutec_eq_bmutec = true;
-
- if (of_get_property(codec->dev->of_node,
- "cirrus,enable-soft-reset", NULL))
- cs4271->enable_soft_reset = true;
+ amutec_eq_bmutec = of_property_read_bool(component->dev->of_node,
+ "cirrus,amutec-eq-bmutec");
+ cs4271->enable_soft_reset = of_property_read_bool(component->dev->of_node,
+ "cirrus,enable-soft-reset");
+
+ 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;
}
-#endif
if (cs4271plat) {
amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
}
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- /* Reset codec */
- gpio_direction_output(cs4271->gpio_nreset, 0);
- udelay(1);
- gpio_set_value(cs4271->gpio_nreset, 1);
- /* Give the codec time to wake up */
- udelay(1);
- }
+ /* 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,
@@ -587,55 +603,58 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs4271_codec_remove(struct snd_soc_codec *codec)
+static void cs4271_component_remove(struct snd_soc_component *component)
{
- struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+ 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);
+ /* Set codec to the reset state */
+ gpiod_set_value(cs4271->reset, 1);
- return 0;
+ regcache_mark_dirty(cs4271->regmap);
+ regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
};
-static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
- .probe = cs4271_codec_probe,
- .remove = cs4271_codec_remove,
+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);
+ cs4271->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+ if (IS_ERR(cs4271->reset))
+ return dev_err_probe(dev, PTR_ERR(cs4271->reset),
+ "error retrieving RESET GPIO\n");
+ gpiod_set_consumer_name(cs4271->reset, "CS4271 Reset");
- if (cs4271plat)
- cs4271->gpio_nreset = cs4271plat->gpio_nreset;
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ cs4271->supplies[i].supply = supply_names[i];
- if (gpio_is_valid(cs4271->gpio_nreset)) {
- int ret;
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies),
+ cs4271->supplies);
- ret = devm_gpio_request(dev, cs4271->gpio_nreset,
- "CS4271 Reset");
- if (ret < 0)
- return ret;
+ if (ret < 0) {
+ dev_err(dev, "Failed to get regulators: %d\n", ret);
+ return ret;
}
*c = cs4271;
@@ -647,8 +666,8 @@ const struct regmap_config cs4271_regmap_config = {
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
+ .cache_type = REGCACHE_FLAT,
+ .val_bits = 8,
.volatile_reg = cs4271_volatile_reg,
};
EXPORT_SYMBOL_GPL(cs4271_regmap_config);
@@ -668,8 +687,8 @@ int cs4271_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, cs4271);
cs4271->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
- 1);
+ return devm_snd_soc_register_component(dev, &soc_component_dev_cs4271,
+ &cs4271_dai, 1);
}
EXPORT_SYMBOL_GPL(cs4271_probe);
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
index 9adad8eefdc9..290283a9149e 100644
--- a/sound/soc/codecs/cs4271.h
+++ b/sound/soc/codecs/cs4271.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _CS4271_PRIV_H
#define _CS4271_PRIV_H
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
new file mode 100644
index 000000000000..98b6718ccabf
--- /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 <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#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 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 = {
+ 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"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l42_id);
+
+static struct i2c_driver cs42l42_i2c_driver = {
+ .driver = {
+ .name = "cs42l42",
+ .pm = pm_ptr(&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 <rf@opensource.cirrus.com>");
+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 000000000000..f837c7eff10b
--- /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 <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of_irq.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+#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 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 cs42l42_soft_reboot_seq[] = {
+ REG_SEQ0(CS42L42_SOFT_RESET_REBOOT, 0x1e),
+};
+
+static int 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(cs42l42_suspend, cs42l42_sdw_resume)
+ 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 = pm_ptr(&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 <rf@opensource.cirrus.com>");
+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 000000000000..78bb093fa0cc
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.c
@@ -0,0 +1,2495 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l42.c -- CS42L42 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <dt-bindings/sound/cs42l42.h>
+
+#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_CBC_CFP:
+ asp_cfg_val |= CS42L42_ASP_MASTER_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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_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,
+ &reg);
+ 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, &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);
+
+ 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, &reg);
+ 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, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>");
+MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Vitaly Rodionov <vitalyr@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
new file mode 100644
index 000000000000..3d85ebc59489
--- /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 <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ */
+
+#ifndef __CS42L42_H__
+#define __CS42L42_H__
+
+#include <dt-bindings/sound/cs42l42.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/jack.h>
+#include <sound/cs42l42.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+
+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 000000000000..867e23d4fb8d
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -0,0 +1,962 @@
+// 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 <linux/build_bug.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-jack.h>
+#include <sound/soc.h>
+
+#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;
+ }
+
+ ret = device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
+ priv->buttons, ret);
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,button-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+ } 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", 50000, &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", 14,
+ &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,
+ &priv->tip_fall_db_ms, 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,
+ &priv->tip_rise_db_ms, 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_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool type_detect)
+{
+ 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 (!type_detect) {
+ if (priv->bias_low)
+ val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ 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);
+ }
+
+ 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);
+
+ 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);
+ }
+}
+
+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(1000));
+
+ 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);
+}
+
+static void cs42l43_stop_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop button detect\n");
+
+ 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_US 11000
+#define CS42L43_BUTTON_COMB_MAX 512
+#define CS42L43_BUTTON_ROUT 2210
+
+irqreturn_t cs42l43_button_press(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct cs42l43 *cs42l43 = priv->core;
+ irqreturn_t iret = IRQ_NONE;
+ 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 iret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!priv->button_detect_running) {
+ dev_dbg(priv->dev, "Spurious button press IRQ\n");
+ goto error;
+ }
+
+ // Wait for 2 full cycles of comb filter to ensure good reading
+ usleep_range(2 * CS42L43_BUTTON_COMB_US, 2 * CS42L43_BUTTON_COMB_US + 50);
+
+ 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);
+
+ iret = IRQ_HANDLED;
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+irqreturn_t cs42l43_button_release(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ irqreturn_t iret = IRQ_NONE;
+ 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 iret;
+ }
+
+ 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);
+
+ iret = IRQ_HANDLED;
+ } else {
+ dev_dbg(priv->dev, "Spurious button release IRQ\n");
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return iret;
+}
+
+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_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 && !priv->hp_ilimited) {
+ 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 && !priv->hp_ilimited) {
+ 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) / USEC_PER_MSEC) + 200;
+ unsigned int type = 0xff;
+ unsigned long time_left;
+
+ reinit_completion(&priv->type_detect);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK);
+
+ 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);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_VAL_MASK, 0);
+
+ 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;
+ }
+}
+
+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_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
+ 0x2 << CS42L43_HSDET_MODE_SHIFT);
+}
+
+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);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ if (cs42l43->sdw && priv->jack_present) {
+ pm_runtime_put(priv->dev);
+ priv->jack_present = false;
+ }
+ }
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ priv->suspend_jack_debounce = false;
+
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_tip_sense(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ unsigned int db_delay = priv->tip_debounce_ms;
+
+ cancel_delayed_work(&priv->bias_sense_timeout);
+ cancel_delayed_work(&priv->tip_sense_work);
+
+ // Ensure delay after suspend is long enough to avoid false detection
+ if (priv->suspend_jack_debounce)
+ db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms;
+
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work,
+ msecs_to_jiffies(db_delay));
+
+ 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",
+};
+
+static_assert(ARRAY_SIZE(cs42l43_jack_override_modes) ==
+ ARRAY_SIZE(cs42l43_jack_text) - 1);
+
+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];
+
+ 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);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+
+ 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 000000000000..336e88a7a987
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-sdw.c
@@ -0,0 +1,71 @@
+// 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 <linux/errno.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <sound/pcm.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+
+#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);
+ 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 <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
new file mode 100644
index 000000000000..b61df09f20cf
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.c
@@ -0,0 +1,2476 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/bitmap.h>
+#include <linux/gcd.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <sound/control.h>
+#include <sound/cs42l43.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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)
+
+static void cs42l43_hp_ilimit_clear_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ hp_ilimit_clear_work.work);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ priv->hp_ilimit_count--;
+
+ if (priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+}
+
+static irqreturn_t cs42l43_hp_ilimit(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(priv->component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "headphone ilimit IRQ\n");
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ilimit_count < CS42L43_HP_ILIMIT_MAX_COUNT) {
+ if (!priv->hp_ilimit_count)
+ queue_delayed_work(system_wq, &priv->hp_ilimit_clear_work,
+ msecs_to_jiffies(CS42L43_HP_ILIMIT_DECAY_MS));
+
+ priv->hp_ilimit_count++;
+ snd_soc_dapm_mutex_unlock(dapm);
+ return IRQ_HANDLED;
+ }
+
+ dev_err(priv->dev, "Disabling headphone for %dmS, due to frequent current limit\n",
+ CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ priv->hp_ilimited = true;
+
+ // No need to wait for disable, as just disabling for a period of time
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ msleep(CS42L43_HP_ILIMIT_BACKOFF_MS);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if (priv->hp_ena && !priv->load_detect_running) {
+ 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, "ilimit HP restore timed out\n");
+ }
+
+ priv->hp_ilimited = false;
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return IRQ_HANDLED;
+}
+
+#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;
+ struct snd_soc_component *component = priv->component;
+ int i;
+
+ dev_dbg(priv->dev, "Microphone shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ for (i = 1; i < ARRAY_SIZE(priv->kctl); i++) {
+ if (!priv->kctl[i])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[i]->id);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs42l43_spk_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ struct snd_soc_component *component = priv->component;
+
+ dev_dbg(priv->dev, "Speaker shutter changed\n");
+
+ if (!component)
+ return IRQ_NONE;
+
+ if (!priv->kctl[0])
+ return IRQ_NONE;
+
+ snd_ctl_notify(component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &priv->kctl[0]->id);
+
+ 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 = !dai->id || !!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 long mask,
+ int *slots, unsigned int nslots)
+{
+ int i = 0;
+ int slot;
+
+ for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) {
+ if (i == nslots) {
+ dev_warn(priv->dev, "Too many channels in TDM mask: %lx\n",
+ mask);
+ return;
+ }
+
+ slots[i++] = slot;
+ }
+
+}
+
+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,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ return 0;
+}
+
+static int cs42l43_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ static const char * const controls[] = {
+ "Speaker Digital Switch",
+ "Decimator 1 Switch",
+ "Decimator 2 Switch",
+ "Decimator 3 Switch",
+ "Decimator 4 Switch",
+ };
+ int i;
+
+ static_assert(ARRAY_SIZE(controls) == ARRAY_SIZE(priv->kctl));
+
+ for (i = 0; i < ARRAY_SIZE(controls); i++) {
+ if (priv->kctl[i])
+ continue;
+
+ priv->kctl[i] = snd_soc_component_get_kcontrol(component, controls[i]);
+ }
+
+ return 0;
+}
+
+static int cs42l43_dai_remove(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->kctl); i++)
+ priv->kctl[i] = NULL;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l43_asp_ops = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .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 = {
+ .probe = cs42l43_dai_probe,
+ .remove = cs42l43_dai_remove,
+ .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_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)
+ ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ 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)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+
+ 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, 4, 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);
+
+ div = fls(freq) -
+ fls(cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq);
+ freq >>= div;
+
+ 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 && !priv->hp_ilimited)
+ 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 || priv->hp_ilimited)
+ 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,
+ ARRAY_SIZE(priv->tx_slots));
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots,
+ ARRAY_SIZE(priv->rx_slots));
+
+ priv->component = component;
+ priv->constraint = cs42l43_constraint;
+
+ return 0;
+}
+
+static void cs42l43_component_remove(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ cs42l43_set_jack(priv->component, NULL, NULL);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ priv->component = NULL;
+}
+
+static const struct snd_soc_component_driver cs42l43_component_drv = {
+ .name = "cs42l43-codec",
+
+ .probe = cs42l43_component_probe,
+ .remove = cs42l43_component_remove,
+ .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,
+ const char * const name, unsigned int irq,
+ irq_handler_t handler, unsigned long flags)
+{
+ int ret;
+
+ ret = irq_create_mapping(priv->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 void cs42l43_disable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ disable_irq(ret);
+}
+
+static void cs42l43_enable_irq(struct cs42l43_codec *priv, unsigned int irq)
+{
+ int ret;
+
+ ret = irq_find_mapping(priv->dom, irq);
+ if (ret > 0)
+ enable_irq(ret);
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv, unsigned int shutter,
+ const char * const open_name, unsigned int *open_irq,
+ const char * const close_name, unsigned int *close_irq,
+ irq_handler_t handler)
+{
+ 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, close_name, *close_irq, handler, IRQF_SHARED);
+ if (ret)
+ return ret;
+
+ return cs42l43_request_irq(priv, 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;
+ unsigned int val;
+ int i, ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->core = cs42l43;
+
+ priv->dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+ if (!priv->dom)
+ return -EPROBE_DEFER;
+
+ 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->hp_ilimit_clear_work, cs42l43_hp_ilimit_clear_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);
+
+ ret = devm_pm_runtime_enable(priv->dev);
+ if (ret)
+ goto err_pm;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
+ ret = cs42l43_request_irq(priv, 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, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+ "mic shutter open", &priv->shutter_irqs[0],
+ "mic shutter close", &priv->shutter_irqs[1],
+ cs42l43_mic_shutter);
+ if (ret)
+ goto err_pm;
+
+ ret = cs42l43_shutter_irq(priv, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+ CS42L43_SPK_SHUTTER_CFG_SHIFT,
+ "spk shutter open", &priv->shutter_irqs[2],
+ "spk shutter close", &priv->shutter_irqs[3],
+ 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_put_autosuspend(priv->dev);
+
+ return 0;
+
+err_clk:
+ clk_put(priv->mclk);
+err_pm:
+ pm_runtime_put_sync(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_codec_remove(struct platform_device *pdev)
+{
+ struct cs42l43_codec *priv = platform_get_drvdata(pdev);
+
+ clk_put(priv->mclk);
+}
+
+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;
+}
+
+static int cs42l43_codec_suspend(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int i;
+
+ dev_dbg(priv->dev, "System suspend\n");
+
+ priv->suspend_jack_debounce = true;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_disable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_disable_irq(priv, priv->shutter_irqs[i]);
+
+ cancel_delayed_work_sync(&priv->bias_sense_timeout);
+ cancel_delayed_work_sync(&priv->tip_sense_work);
+ cancel_delayed_work_sync(&priv->hp_ilimit_clear_work);
+
+ cs42l43_clear_jack(priv);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int cs42l43_codec_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+ int ret, i;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++)
+ cs42l43_enable_irq(priv, cs42l43_irqs[i].irq);
+
+ for (i = 0; i < ARRAY_SIZE(priv->shutter_irqs); i++)
+ if (priv->shutter_irqs[i])
+ cs42l43_enable_irq(priv, priv->shutter_irqs[i]);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs42l43_codec_pm_ops = {
+ RUNTIME_PM_OPS(NULL, cs42l43_codec_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs42l43_codec_suspend, cs42l43_codec_resume)
+};
+
+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 = pm_ptr(&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 <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
new file mode 100644
index 000000000000..b2fa2cd1d99f
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.h
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS42L43 CODEC driver internal data
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS42L43_ASOC_INT_H
+#define CS42L43_ASOC_INT_H
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.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_HP_ILIMIT_BACKOFF_MS 1000
+#define CS42L43_HP_ILIMIT_DECAY_MS 300
+#define CS42L43_HP_ILIMIT_MAX_COUNT 4
+
+#define CS42L43_ASP_MAX_CHANNELS 6
+#define CS42L43_N_EQ_COEFFS 15
+
+#define CS42L43_N_BUTTONS 6
+
+struct clk;
+struct device;
+
+struct snd_soc_component;
+struct snd_soc_jack;
+
+struct cs42l43;
+
+struct cs42l43_codec {
+ struct device *dev;
+ struct cs42l43 *core;
+ struct snd_soc_component *component;
+ struct irq_domain *dom;
+ unsigned int shutter_irqs[4];
+
+ 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 tip_fall_db_ms;
+ unsigned int tip_rise_db_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 completion type_detect;
+ struct completion load_detect;
+
+ bool load_detect_running;
+ bool button_detect_running;
+ bool jack_present;
+ int jack_override;
+ bool suspend_jack_debounce;
+
+ struct delayed_work hp_ilimit_clear_work;
+ bool hp_ilimited;
+ int hp_ilimit_count;
+
+ struct snd_kcontrol *kctl[5];
+};
+
+#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_clear_jack(struct cs42l43_codec *priv);
+void cs42l43_tip_sense_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
index c40428f25ba5..ba7e237619f2 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l56.c -- CS42L51 ALSA SoC I2C audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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.
- *
*/
#include <linux/i2c.h>
@@ -17,36 +13,41 @@
#include "cs42l51.h"
-static struct i2c_device_id cs42l51_i2c_id[] = {
- {"cs42l51", 0},
- {}
+static const struct i2c_device_id cs42l51_i2c_id[] = {
+ { "cs42l51" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
-static int cs42l51_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *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;
- config.val_bits = 8;
- config.reg_bits = 8;
return cs42l51_probe(&i2c->dev, devm_regmap_init_i2c(i2c, &config));
}
-static int cs42l51_i2c_remove(struct i2c_client *i2c)
+static void cs42l51_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
+ cs42l51_remove(&i2c->dev);
}
+static const struct dev_pm_ops cs42l51_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(cs42l51_suspend, cs42l51_resume)
+};
+
static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
- .owner = THIS_MODULE,
.of_match_table = cs42l51_of_match,
+ .pm = &cs42l51_pm_ops,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index b3951524339f..8083a339dc7b 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l51.c
*
@@ -7,20 +8,12 @@
*
* Based on cs4270.c - Copyright (c) Freescale Semiconductor
*
- * 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.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* For now:
* - Only I2C is support. Not SPI
* - master mode *NOT* supported
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -29,7 +22,9 @@
#include <sound/initval.h>
#include <sound/pcm_params.h>
#include <sound/pcm.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include "cs42l51.h"
@@ -39,36 +34,44 @@ enum master_slave_mode {
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_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
+#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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- unsigned long value = snd_soc_read(codec, CS42L51_PCM_MIXER)&3;
+ 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.integer.value[0] = 0;
+ ucontrol->value.enumerated.item[0] = 0;
break;
/* same value : (L+R)/2 and (R+L)/2 */
case 1:
case 2:
- ucontrol->value.integer.value[0] = 1;
+ ucontrol->value.enumerated.item[0] = 1;
break;
case 3:
- ucontrol->value.integer.value[0] = 2;
+ ucontrol->value.enumerated.item[0] = 2;
break;
}
@@ -82,10 +85,10 @@ static int cs42l51_get_chan_mix(struct snd_kcontrol *kcontrol,
static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
unsigned char val;
- switch (ucontrol->value.integer.value[0]) {
+ switch (ucontrol->value.enumerated.item[0]) {
default:
case 0:
val = CHAN_MIX_NORMAL;
@@ -98,7 +101,7 @@ static int cs42l51_set_chan_mix(struct snd_kcontrol *kcontrol,
break;
}
- snd_soc_write(codec, CS42L51_PCM_MIXER, val);
+ snd_soc_component_write(component, CS42L51_PCM_MIXER, val);
return 1;
}
@@ -109,12 +112,16 @@ 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[] = {
@@ -131,12 +138,20 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
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",
@@ -153,17 +168,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
+ 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_update_bits(codec, CS42L51_POWER_CTL1,
+ snd_soc_component_update_bits(component, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, 0);
break;
}
@@ -193,7 +208,8 @@ 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_MICBIAS("Mic Bias", CS42L51_MIC_POWER_CTL, 1, 1),
+ 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,
@@ -204,12 +220,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
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", "Left HiFi Playback",
- CS42L51_POWER_CTL1, 5, 1,
- cs42l51_pdn_event, SND_SOC_DAPM_PRE_POST_PMD),
- SND_SOC_DAPM_DAC_E("Right DAC", "Right HiFi Playback",
- CS42L51_POWER_CTL1, 6, 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"),
@@ -237,10 +251,40 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = {
&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"},
@@ -263,8 +307,8 @@ static const struct snd_soc_dapm_route cs42l51_routes[] = {
static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -273,19 +317,19 @@ static int cs42l51_set_dai_fmt(struct snd_soc_dai *codec_dai,
cs42l51->audio_mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
- dev_err(codec->dev, "invalid DAI format\n");
+ dev_err(component->dev, "invalid DAI format\n");
return -EINVAL;
}
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l51->func = MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l51->func = MODE_SLAVE_AUTO;
break;
default:
- dev_err(codec->dev, "Unknown master/slave configuration\n");
+ dev_err(component->dev, "Unknown master/slave configuration\n");
return -EINVAL;
}
@@ -323,11 +367,24 @@ static struct cs42l51_ratios slave_auto_ratios[] = {
{ 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_codec *codec = codec_dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l51_private *cs42l51 = snd_soc_component_get_drvdata(component);
cs42l51->mclk = freq;
return 0;
@@ -337,19 +394,21 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l51_private *cs42l51 = snd_soc_codec_get_drvdata(codec);
+ 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;
+ int intf_ctl, power_ctl, fmt, mode;
switch (cs42l51->func) {
case MODE_MASTER:
- return -EINVAL;
+ ratios = master_ratios;
+ nr_ratios = ARRAY_SIZE(master_ratios);
+ break;
case MODE_SLAVE:
ratios = slave_ratios;
nr_ratios = ARRAY_SIZE(slave_ratios);
@@ -370,12 +429,12 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
if (i == nr_ratios) {
/* We did not find a matching ratio */
- dev_err(codec->dev, "could not find matching ratio\n");
+ dev_err(component->dev, "could not find matching ratio\n");
return -EINVAL;
}
- intf_ctl = snd_soc_read(codec, CS42L51_INTF_CTL);
- power_ctl = snd_soc_read(codec, CS42L51_MIC_POWER_CTL);
+ 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));
@@ -385,7 +444,16 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
switch (cs42l51->func) {
case MODE_MASTER:
intf_ctl |= CS42L51_INTF_CTL_MASTER;
- power_ctl |= CS42L51_MIC_POWER_CTL_SPEED(ratios[i].speed_mode);
+ 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);
@@ -418,51 +486,59 @@ static int cs42l51_hw_params(struct snd_pcm_substream *substream,
fmt = CS42L51_DAC_DIF_RJ24;
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
intf_ctl |= CS42L51_INTF_CTL_DAC_FORMAT(fmt);
break;
default:
- dev_err(codec->dev, "unknown format\n");
+ dev_err(component->dev, "unknown format\n");
return -EINVAL;
}
if (ratios[i].mclk)
power_ctl |= CS42L51_MIC_POWER_CTL_MCLK_DIV2;
- ret = snd_soc_write(codec, CS42L51_INTF_CTL, intf_ctl);
+ ret = snd_soc_component_write(component, CS42L51_INTF_CTL, intf_ctl);
if (ret < 0)
return ret;
- ret = snd_soc_write(codec, CS42L51_MIC_POWER_CTL, power_ctl);
+ 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)
+static int cs42l51_dai_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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_read(codec, CS42L51_DAC_OUT_CTL);
+ reg = snd_soc_component_read(component, CS42L51_DAC_OUT_CTL);
if (mute)
reg |= mask;
else
reg &= ~mask;
- return snd_soc_write(codec, CS42L51_DAC_OUT_CTL, reg);
+ 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,
- .digital_mute = cs42l51_dai_mute,
+ .mute_stream = cs42l51_dai_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42l51_dai = {
@@ -484,9 +560,17 @@ static struct snd_soc_dai_driver cs42l51_dai = {
.ops = &cs42l51_dai_ops,
};
-static int cs42l51_codec_probe(struct snd_soc_codec *codec)
+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
@@ -497,27 +581,129 @@ static int cs42l51_codec_probe(struct snd_soc_codec *codec)
*/
reg = CS42L51_DAC_CTL_DATA_SEL(1)
| CS42L51_DAC_CTL_AMUTE | CS42L51_DAC_CTL_DACSZ(0);
- ret = snd_soc_write(codec, CS42L51_DAC_CTL, reg);
+ ret = snd_soc_component_write(component, CS42L51_DAC_CTL, reg);
if (ret < 0)
return ret;
return 0;
}
-static struct snd_soc_codec_driver soc_codec_device_cs42l51 = {
- .probe = cs42l51_codec_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),
+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_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42l51_regmap);
@@ -525,7 +711,7 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
{
struct cs42l51_private *cs42l51;
unsigned int val;
- int ret;
+ int ret, i;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -536,6 +722,41 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
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)) {
+ ret = PTR_ERR(cs42l51->reset_gpio);
+ goto error;
+ }
+
+ 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);
@@ -553,19 +774,57 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "Cirrus Logic CS42L51, Revision: %02X\n",
val & CS42L51_CHIP_REV_MASK);
- ret = snd_soc_register_codec(dev,
- &soc_codec_device_cs42l51, &cs42l51_dai, 1);
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_device_cs42l51, &cs42l51_dai, 1);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
error:
+ gpiod_set_value_cansleep(cs42l51->reset_gpio, 1);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l51->supplies),
+ cs42l51->supplies);
return ret;
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-const struct of_device_id cs42l51_of_match[] = {
- { .compatible = "cirrus,cs42l51", },
- { }
-};
-MODULE_DEVICE_TABLE(of, cs42l51_of_match);
-EXPORT_SYMBOL_GPL(cs42l51_of_match);
+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 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 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 <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index 0ca805492ac4..125703ede113 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -1,19 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cs42l51.h
*
* ASoC Driver for Cirrus Logic CS42L51 codecs
*
* Copyright (c) 2010 Arnaud Patard <apatard@mandriva.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#ifndef _CS42L51_H
#define _CS42L51_H
@@ -22,7 +13,9 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
-extern const struct of_device_id cs42l51_of_match[];
+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
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 1589e7a881d8..a9ffba62aaf8 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l52.c -- CS42L52 ALSA SoC audio driver
*
@@ -5,34 +6,28 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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.
- *
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/of_gpio.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l52.h>
#include "cs42l52.h"
struct sp_config {
@@ -40,9 +35,27 @@ struct sp_config {
u32 srate;
};
+struct cs42l52_platform_data {
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* MICA mode selection Differential or Single-ended */
+ bool mica_diff_cfg;
+
+ /* MICB mode selection Differential or Single-ended */
+ bool micb_diff_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg73 */
+ unsigned int chgfreq;
+
+ /* Reset GPIO */
+ struct gpio_desc *reset_gpio;
+};
+
struct cs42l52_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct sp_config config;
struct cs42l52_platform_data pdata;
@@ -110,58 +123,7 @@ static const struct reg_default cs42l52_reg_defaults[] = {
static bool cs42l52_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L52_CHIP:
- case CS42L52_PWRCTL1:
- case CS42L52_PWRCTL2:
- case CS42L52_PWRCTL3:
- case CS42L52_CLK_CTL:
- case CS42L52_IFACE_CTL1:
- case CS42L52_IFACE_CTL2:
- case CS42L52_ADC_PGA_A:
- case CS42L52_ADC_PGA_B:
- case CS42L52_ANALOG_HPF_CTL:
- case CS42L52_ADC_HPF_FREQ:
- case CS42L52_ADC_MISC_CTL:
- case CS42L52_PB_CTL1:
- case CS42L52_MISC_CTL:
- case CS42L52_PB_CTL2:
- case CS42L52_MICA_CTL:
- case CS42L52_MICB_CTL:
- case CS42L52_PGAA_CTL:
- case CS42L52_PGAB_CTL:
- case CS42L52_PASSTHRUA_VOL:
- case CS42L52_PASSTHRUB_VOL:
- case CS42L52_ADCA_VOL:
- case CS42L52_ADCB_VOL:
- case CS42L52_ADCA_MIXER_VOL:
- case CS42L52_ADCB_MIXER_VOL:
- case CS42L52_PCMA_MIXER_VOL:
- case CS42L52_PCMB_MIXER_VOL:
- case CS42L52_BEEP_FREQ:
- case CS42L52_BEEP_VOL:
- case CS42L52_BEEP_TONE_CTL:
- case CS42L52_TONE_CTL:
- case CS42L52_MASTERA_VOL:
- case CS42L52_MASTERB_VOL:
- case CS42L52_HPA_VOL:
- case CS42L52_HPB_VOL:
- case CS42L52_SPKA_VOL:
- case CS42L52_SPKB_VOL:
- case CS42L52_ADC_PCM_MIXER:
- case CS42L52_LIMITER_CTL1:
- case CS42L52_LIMITER_CTL2:
- case CS42L52_LIMITER_AT_RATE:
- case CS42L52_ALC_CTL:
- case CS42L52_ALC_RATE:
- case CS42L52_ALC_THRESHOLD:
- case CS42L52_NOISE_GATE_CTL:
- case CS42L52_CLK_STATUS:
- case CS42L52_BATT_COMPEN:
- case CS42L52_BATT_LEVEL:
- case CS42L52_SPK_STATUS:
- case CS42L52_TEM_CTL:
- case CS42L52_THE_FOLDBACK:
- case CS42L52_CHARGE_PUMP:
+ case CS42L52_CHIP ... CS42L52_CHARGE_PUMP:
return true;
default:
return false;
@@ -192,15 +154,16 @@ 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(mix_tlv, -50, 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 unsigned int limiter_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const char * const cs42l52_adca_text[] = {
"Input1A", "Input2A", "Input3A", "Input4A", "PGA Input Left"};
@@ -407,7 +370,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
- CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pga_tlv),
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pass_tlv),
SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
@@ -420,7 +383,7 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
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, ipd_tlv),
+ 0, 0x19, 0x7F, mix_tlv),
SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
@@ -525,17 +488,17 @@ static const struct snd_kcontrol_new cs42l52_micb_controls[] = {
SOC_ENUM("MICB Select", micb_enum),
};
-static int cs42l52_add_mic_controls(struct snd_soc_codec *codec)
+static int cs42l52_add_mic_controls(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_controls(codec, cs42l52_mica_controls,
+ snd_soc_add_component_controls(component, cs42l52_mica_controls,
ARRAY_SIZE(cs42l52_mica_controls));
if (!pdata->micb_diff_cfg)
- snd_soc_add_codec_controls(codec, cs42l52_micb_controls,
+ snd_soc_add_component_controls(component, cs42l52_micb_controls,
ARRAY_SIZE(cs42l52_micb_controls));
return 0;
@@ -768,13 +731,13 @@ static int cs42l52_get_clk(int mclk, int rate)
static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid freq parameter\n");
+ dev_err(component->dev, "Invalid freq parameter\n");
return -EINVAL;
}
return 0;
@@ -782,15 +745,15 @@ static int cs42l52_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
iface = CS42L52_IFACE_CTL1_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface = CS42L52_IFACE_CTL1_SLAVE;
break;
default:
@@ -835,21 +798,21 @@ static int cs42l52_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
cs42l52->config.format = iface;
- snd_soc_write(codec, CS42L52_IFACE_CTL1, cs42l52->config.format);
+ snd_soc_component_write(component, CS42L52_IFACE_CTL1, cs42l52->config.format);
return 0;
}
-static int cs42l52_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l52_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute)
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_MUTE);
else
- snd_soc_update_bits(codec, CS42L52_PB_CTL1,
+ snd_soc_component_update_bits(component, CS42L52_PB_CTL1,
CS42L52_PB_CTL1_MUTE_MASK,
CS42L52_PB_CTL1_UNMUTE);
@@ -860,8 +823,8 @@ 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_codec *codec = dai->codec;
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
u32 clk = 0;
int index;
@@ -875,40 +838,39 @@ static int cs42l52_pcm_hw_params(struct snd_pcm_substream *substream,
(clk_map_table[index].ratio << CLK_RATIO_SHIFT) |
clk_map_table[index].mclkdiv2;
- snd_soc_write(codec, CS42L52_CLK_CTL, clk);
+ snd_soc_component_write(component, CS42L52_CLK_CTL, clk);
} else {
- dev_err(codec->dev, "can't get correct mclk\n");
+ dev_err(component->dev, "can't get correct mclk\n");
return -EINVAL;
}
return 0;
}
-static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l52_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L52_PWRCTL1,
+ snd_soc_component_update_bits(component, CS42L52_PWRCTL1,
CS42L52_PWRCTL1_PDN_CODEC, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l52->regmap, false);
regcache_sync(cs42l52->regmap);
}
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
+ snd_soc_component_write(component, CS42L52_PWRCTL1, CS42L52_PWRCTL1_PDN_ALL);
regcache_cache_only(cs42l52->regmap, true);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -920,11 +882,12 @@ static int cs42l52_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
-static struct snd_soc_dai_ops cs42l52_ops = {
+static const struct snd_soc_dai_ops cs42l52_ops = {
.hw_params = cs42l52_pcm_hw_params,
- .digital_mute = cs42l52_digital_mute,
+ .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 = {
@@ -955,8 +918,8 @@ static void cs42l52_beep_work(struct work_struct *work)
{
struct cs42l52_private *cs42l52 =
container_of(work, struct cs42l52_private, beep_work);
- struct snd_soc_codec *codec = cs42l52->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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;
@@ -968,18 +931,18 @@ static void cs42l52_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ 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(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L52_BEEP_FREQ,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_FREQ,
CS42L52_BEEP_RATE_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -991,15 +954,16 @@ static void cs42l52_beep_work(struct work_struct *work)
static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ 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:
@@ -1012,9 +976,8 @@ static int cs42l52_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l52_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+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;
@@ -1029,16 +992,16 @@ static ssize_t cs42l52_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l52_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l52_init_beep(struct snd_soc_codec *codec)
+static void cs42l52_init_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l52->beep = devm_input_allocate_device(codec->dev);
+ cs42l52->beep = devm_input_allocate_device(component->dev);
if (!cs42l52->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -1046,49 +1009,49 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
cs42l52->beep_rate = 0;
cs42l52->beep->name = "CS42L52 Beep Generator";
- cs42l52->beep->phys = dev_name(codec->dev);
+ 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 = codec->dev;
- input_set_drvdata(cs42l52->beep, codec);
+ 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(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l52_free_beep(struct snd_soc_codec *codec)
+static void cs42l52_free_beep(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l52->beep_work);
cs42l52->beep = NULL;
- snd_soc_update_bits(codec, CS42L52_BEEP_TONE_CTL,
+ snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL,
CS42L52_BEEP_EN_MASK, 0);
}
-static int cs42l52_probe(struct snd_soc_codec *codec)
+static int cs42l52_probe(struct snd_soc_component *component)
{
- struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
regcache_cache_only(cs42l52->regmap, true);
- cs42l52_add_mic_controls(codec);
+ cs42l52_add_mic_controls(component);
- cs42l52_init_beep(codec);
+ cs42l52_init_beep(component);
cs42l52->sysclk = CS42L52_DEFAULT_CLK;
cs42l52->config.format = CS42L52_DEFAULT_FORMAT;
@@ -1096,30 +1059,29 @@ static int cs42l52_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs42l52_remove(struct snd_soc_codec *codec)
+static void cs42l52_remove(struct snd_soc_component *component)
{
- cs42l52_free_beep(codec);
-
- return 0;
+ cs42l52_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
- .probe = cs42l52_probe,
- .remove = cs42l52_remove,
- .set_bias_level = cs42l52_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
-
- .controls = cs42l52_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l52_snd_controls),
+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_default cs42l52_threshold_patch[] = {
+static const struct reg_sequence cs42l52_threshold_patch[] = {
{ 0x00, 0x99 },
{ 0x3E, 0xBA },
@@ -1139,21 +1101,19 @@ static const struct regmap_config cs42l52_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l52_reg_defaults),
.readable_reg = cs42l52_readable_register,
.volatile_reg = cs42l52_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
-static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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);
+ struct cs42l52_platform_data *pdata;
int ret;
- unsigned int devid = 0;
+ unsigned int devid;
unsigned int reg;
u32 val32;
- cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l52_private),
- GFP_KERNEL);
+ cs42l52 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l52), GFP_KERNEL);
if (cs42l52 == NULL)
return -ENOMEM;
cs42l52->dev = &i2c_client->dev;
@@ -1164,52 +1124,43 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
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(struct cs42l52_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
- 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;
+
+ 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 = devm_gpiod_get_optional(&i2c_client->dev,
+ "cirrus,reset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L52 /RST");
}
+ 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);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
}
i2c_set_clientdata(i2c_client, cs42l52);
@@ -1221,6 +1172,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
ret);
ret = regmap_read(cs42l52->regmap, CS42L52_CHIP, &reg);
+ 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;
@@ -1257,17 +1213,8 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
CS42L52_IFACE_CTL2_BIAS_LVL,
cs42l52->pdata.micbias_lvl);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l52, &cs42l52_dai, 1);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-static int cs42l52_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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[] = {
@@ -1278,7 +1225,7 @@ MODULE_DEVICE_TABLE(of, cs42l52_of_match);
static const struct i2c_device_id cs42l52_id[] = {
- { "cs42l52", 0 },
+ { "cs42l52" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l52_id);
@@ -1286,12 +1233,10 @@ MODULE_DEVICE_TABLE(i2c, cs42l52_id);
static struct i2c_driver cs42l52_i2c_driver = {
.driver = {
.name = "cs42l52",
- .owner = THIS_MODULE,
.of_match_table = cs42l52_of_match,
},
.id_table = cs42l52_id,
- .probe = cs42l52_i2c_probe,
- .remove = cs42l52_i2c_remove,
+ .probe = cs42l52_i2c_probe,
};
module_i2c_driver(cs42l52_i2c_driver);
diff --git a/sound/soc/codecs/cs42l52.h b/sound/soc/codecs/cs42l52.h
index ac445993e6bf..e485670f9a6f 100644
--- a/sound/soc/codecs/cs42l52.h
+++ b/sound/soc/codecs/cs42l52.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l52.h -- CS42L52 ALSA SoC audio driver
*
@@ -5,11 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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 __CS42L52_H__
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index cbc654fe48c7..98fa812bc07b 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1,42 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l56.c -- CS42L56 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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.
- *
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l56.h>
#include "cs42l56.h"
#define CS42L56_NUM_SUPPLIES 3
+
+struct cs42l56_platform_data {
+ /* GPIO for Reset */
+ struct gpio_desc *gpio_nreset;
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1a_ref_cfg;
+
+ /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2a_ref_cfg;
+
+ /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1b_ref_cfg;
+
+ /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2b_ref_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg62 */
+ unsigned int chgfreq;
+
+ /* HighPass Filter Right Channel Corner Frequency */
+ unsigned int hpfb_freq;
+
+ /* HighPass Filter Left Channel Corner Frequency */
+ unsigned int hpfa_freq;
+
+ /* Adaptive Power Control for LO/HP */
+ unsigned int adaptive_pwr;
+};
+
static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
"VA",
"VCP",
@@ -45,7 +73,7 @@ static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
struct cs42l56_private {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
struct device *dev;
struct cs42l56_platform_data pdata;
struct regulator_bulk_data supplies[CS42L56_NUM_SUPPLIES];
@@ -56,7 +84,7 @@ struct cs42l56_private {
u8 iface;
u8 iface_fmt;
u8 iface_inv;
-#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
+#if IS_ENABLED(CONFIG_INPUT)
struct input_dev *beep;
struct work_struct beep_work;
int beep_rate;
@@ -64,8 +92,6 @@ struct cs42l56_private {
};
static const struct reg_default cs42l56_reg_defaults[] = {
- { 1, 0x56 }, /* r01 - ID 1 */
- { 2, 0x04 }, /* r02 - ID 2 */
{ 3, 0x7f }, /* r03 - Power Ctl 1 */
{ 4, 0xff }, /* r04 - Power Ctl 2 */
{ 5, 0x00 }, /* ro5 - Clocking Ctl 1 */
@@ -115,52 +141,7 @@ static const struct reg_default cs42l56_reg_defaults[] = {
static bool cs42l56_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L56_CHIP_ID_1:
- case CS42L56_CHIP_ID_2:
- case CS42L56_PWRCTL_1:
- case CS42L56_PWRCTL_2:
- case CS42L56_CLKCTL_1:
- case CS42L56_CLKCTL_2:
- case CS42L56_SERIAL_FMT:
- case CS42L56_CLASSH_CTL:
- case CS42L56_MISC_CTL:
- case CS42L56_INT_STATUS:
- case CS42L56_PLAYBACK_CTL:
- case CS42L56_DSP_MUTE_CTL:
- case CS42L56_ADCA_MIX_VOLUME:
- case CS42L56_ADCB_MIX_VOLUME:
- case CS42L56_PCMA_MIX_VOLUME:
- case CS42L56_PCMB_MIX_VOLUME:
- case CS42L56_ANAINPUT_ADV_VOLUME:
- case CS42L56_DIGINPUT_ADV_VOLUME:
- case CS42L56_MASTER_A_VOLUME:
- case CS42L56_MASTER_B_VOLUME:
- case CS42L56_BEEP_FREQ_ONTIME:
- case CS42L56_BEEP_FREQ_OFFTIME:
- case CS42L56_BEEP_TONE_CFG:
- case CS42L56_TONE_CTL:
- case CS42L56_CHAN_MIX_SWAP:
- case CS42L56_AIN_REFCFG_ADC_MUX:
- case CS42L56_HPF_CTL:
- case CS42L56_MISC_ADC_CTL:
- case CS42L56_GAIN_BIAS_CTL:
- case CS42L56_PGAA_MUX_VOLUME:
- case CS42L56_PGAB_MUX_VOLUME:
- case CS42L56_ADCA_ATTENUATOR:
- case CS42L56_ADCB_ATTENUATOR:
- case CS42L56_ALC_EN_ATTACK_RATE:
- case CS42L56_ALC_RELEASE_RATE:
- case CS42L56_ALC_THRESHOLD:
- case CS42L56_NOISE_GATE_CTL:
- case CS42L56_ALC_LIM_SFT_ZC:
- case CS42L56_AMUTE_HPLO_MUX:
- case CS42L56_HPA_VOLUME:
- case CS42L56_HPB_VOLUME:
- case CS42L56_LOA_VOLUME:
- case CS42L56_LOB_VOLUME:
- case CS42L56_LIM_THRESHOLD_CTL:
- case CS42L56_LIM_CTL_RELEASE_RATE:
- case CS42L56_LIM_ATTACK_RATE:
+ case CS42L56_CHIP_ID_1 ... CS42L56_LIM_ATTACK_RATE:
return true;
default:
return false;
@@ -185,21 +166,18 @@ 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 unsigned int ngnb_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int ngb_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+ 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 unsigned int alc_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+ 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),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const char * const beep_config_text[] = {
"Off", "Single", "Multiple", "Continuous"
@@ -253,14 +231,6 @@ 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 adc_swap_text[] = {
- "None", "A+B/2", "A-B/2", "Swap"
-};
-
-static const struct soc_enum adc_swap_enum =
- SOC_ENUM_SINGLE(CS42L56_MISC_ADC_CTL, 3,
- ARRAY_SIZE(adc_swap_text), adc_swap_text);
-
static const char * const pgaa_mux_text[] = {
"AIN1A", "AIN2A", "AIN3A"};
@@ -453,9 +423,9 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
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, 0x84, 0x48, hl_tlv),
+ CS42L56_HPB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
- CS42L56_LOB_VOLUME, 0, 0x84, 0x48, hl_tlv),
+ CS42L56_LOB_VOLUME, 0, 0x44, 0x48, hl_tlv),
SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
0, 0x00, 1, tone_tlv),
@@ -776,8 +746,8 @@ static int cs42l56_get_mclk_ratio(int mclk, int rate)
static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
switch (freq) {
case CS42L56_MCLK_5P6448MHZ:
@@ -803,10 +773,10 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
}
cs42l56->mclk = freq;
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_PREDIV_MASK,
cs42l56->mclk_prediv);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIV2_MASK,
cs42l56->mclk_div2);
@@ -815,14 +785,14 @@ static int cs42l56_set_sysclk(struct snd_soc_dai *codec_dai,
static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42l56->iface = CS42L56_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42l56->iface = CS42L56_SLAVE_MODE;
break;
default:
@@ -853,22 +823,22 @@ static int cs42l56_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MS_MODE_MASK, cs42l56->iface);
- snd_soc_update_bits(codec, CS42L56_SERIAL_FMT,
+ snd_soc_component_update_bits(component, CS42L56_SERIAL_FMT,
CS42L56_DIG_FMT_MASK, cs42l56->iface_fmt);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_SCLK_INV_MASK, cs42l56->iface_inv);
return 0;
}
-static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42l56_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
/* Hit the DSP Mixer first */
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -877,21 +847,21 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_MUTE_ALL);
/* Mute ADC's */
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ 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_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_MUTE_ALL);
} else {
- snd_soc_update_bits(codec, CS42L56_DSP_MUTE_CTL,
+ snd_soc_component_update_bits(component, CS42L56_DSP_MUTE_CTL,
CS42L56_ADCAMIX_MUTE_MASK |
CS42L56_ADCBMIX_MUTE_MASK |
CS42L56_PCMAMIX_MUTE_MASK |
@@ -900,18 +870,18 @@ static int cs42l56_digital_mute(struct snd_soc_dai *dai, int mute)
CS42L56_MSTA_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_MISC_ADC_CTL,
+ snd_soc_component_update_bits(component, CS42L56_MISC_ADC_CTL,
CS42L56_ADCA_MUTE_MASK |
CS42L56_ADCB_MUTE_MASK,
CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPA_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_HPB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_HPB_VOLUME,
CS42L56_HP_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOA_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOA_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
- snd_soc_update_bits(codec, CS42L56_LOB_VOLUME,
+ snd_soc_component_update_bits(component, CS42L56_LOB_VOLUME,
CS42L56_LO_MUTE_MASK, CS42L56_UNMUTE);
}
return 0;
@@ -921,39 +891,39 @@ 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_codec *codec = dai->codec;
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L56_CLKCTL_2,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_2,
CS42L56_CLK_RATIO_MASK, ratio);
} else {
- dev_err(codec->dev, "unsupported mclk/sclk/lrclk ratio\n");
+ dev_err(component->dev, "unsupported mclk/sclk/lrclk ratio\n");
return -EINVAL;
}
return 0;
}
-static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l56_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, CS42L56_CLKCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_CLKCTL_1,
CS42L56_MCLK_DIS_MASK, 0);
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 0);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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),
@@ -965,20 +935,19 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L56_PWRCTL_1,
+ snd_soc_component_update_bits(component, CS42L56_PWRCTL_1,
CS42L56_PDN_ALL_MASK, 1);
- snd_soc_update_bits(codec, CS42L56_CLKCTL_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;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -990,11 +959,12 @@ static int cs42l56_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops cs42l56_ops = {
+static const struct snd_soc_dai_ops cs42l56_ops = {
.hw_params = cs42l56_pcm_hw_params,
- .digital_mute = cs42l56_digital_mute,
+ .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 = {
@@ -1025,8 +995,8 @@ static void cs42l56_beep_work(struct work_struct *work)
{
struct cs42l56_private *cs42l56 =
container_of(work, struct cs42l56_private, beep_work);
- struct snd_soc_codec *codec = cs42l56->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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;
@@ -1038,18 +1008,18 @@ static void cs42l56_beep_work(struct work_struct *work)
best = i;
}
- dev_dbg(codec->dev, "Set beep rate %dHz for requested %dHz\n",
+ 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(codec->dev, "Disabling beep\n");
+ dev_dbg(component->dev, "Disabling beep\n");
snd_soc_dapm_disable_pin(dapm, "Beep");
}
- snd_soc_update_bits(codec, CS42L56_BEEP_FREQ_ONTIME,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_FREQ_ONTIME,
CS42L56_BEEP_FREQ_MASK, val);
snd_soc_dapm_sync(dapm);
@@ -1061,15 +1031,16 @@ static void cs42l56_beep_work(struct work_struct *work)
static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
unsigned int code, int hz)
{
- struct snd_soc_codec *codec = input_get_drvdata(dev);
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = input_get_drvdata(dev);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "Beep event %x %x\n", code, hz);
+ 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:
@@ -1082,9 +1053,8 @@ static int cs42l56_beep_event(struct input_dev *dev, unsigned int type,
return 0;
}
-static ssize_t cs42l56_beep_set(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+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;
@@ -1099,16 +1069,16 @@ static ssize_t cs42l56_beep_set(struct device *dev,
return count;
}
-static DEVICE_ATTR(beep, 0200, NULL, cs42l56_beep_set);
+static DEVICE_ATTR_WO(beep);
-static void cs42l56_init_beep(struct snd_soc_codec *codec)
+static void cs42l56_init_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l56->beep = devm_input_allocate_device(codec->dev);
+ cs42l56->beep = devm_input_allocate_device(component->dev);
if (!cs42l56->beep) {
- dev_err(codec->dev, "Failed to allocate beep device\n");
+ dev_err(component->dev, "Failed to allocate beep device\n");
return;
}
@@ -1116,67 +1086,66 @@ static void cs42l56_init_beep(struct snd_soc_codec *codec)
cs42l56->beep_rate = 0;
cs42l56->beep->name = "CS42L56 Beep Generator";
- cs42l56->beep->phys = dev_name(codec->dev);
+ 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 = codec->dev;
- input_set_drvdata(cs42l56->beep, codec);
+ 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(codec->dev, "Failed to register beep device\n");
+ dev_err(component->dev, "Failed to register beep device\n");
}
- ret = device_create_file(codec->dev, &dev_attr_beep);
+ ret = device_create_file(component->dev, &dev_attr_beep);
if (ret != 0) {
- dev_err(codec->dev, "Failed to create keyclick file: %d\n",
+ dev_err(component->dev, "Failed to create keyclick file: %d\n",
ret);
}
}
-static void cs42l56_free_beep(struct snd_soc_codec *codec)
+static void cs42l56_free_beep(struct snd_soc_component *component)
{
- struct cs42l56_private *cs42l56 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
- device_remove_file(codec->dev, &dev_attr_beep);
+ device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l56->beep_work);
cs42l56->beep = NULL;
- snd_soc_update_bits(codec, CS42L56_BEEP_TONE_CFG,
+ snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG,
CS42L56_BEEP_EN_MASK, 0);
}
-static int cs42l56_probe(struct snd_soc_codec *codec)
+static int cs42l56_probe(struct snd_soc_component *component)
{
- cs42l56_init_beep(codec);
+ cs42l56_init_beep(component);
return 0;
}
-static int cs42l56_remove(struct snd_soc_codec *codec)
+static void cs42l56_remove(struct snd_soc_component *component)
{
- cs42l56_free_beep(codec);
-
- return 0;
+ cs42l56_free_beep(component);
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
- .probe = cs42l56_probe,
- .remove = cs42l56_remove,
- .set_bias_level = cs42l56_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
-
- .controls = cs42l56_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l56_snd_controls),
+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 = {
@@ -1188,7 +1157,7 @@ static const struct regmap_config cs42l56_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l56_reg_defaults),
.readable_reg = cs42l56_readable_register,
.volatile_reg = cs42l56_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
@@ -1224,25 +1193,26 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
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);
+ pdata->gpio_nreset = devm_gpiod_get_optional(&i2c_client->dev, "cirrus,gpio-nreset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->gpio_nreset))
+ return PTR_ERR(pdata->gpio_nreset);
+
+ gpiod_set_consumer_name(pdata->gpio_nreset, "CS42L56 /RST");
return 0;
}
-static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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 = 0;
+ unsigned int devid;
unsigned int alpha_rev, metal_rev;
unsigned int reg;
- cs42l56 = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l56_private),
- GFP_KERNEL);
+ cs42l56 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l56), GFP_KERNEL);
if (cs42l56 == NULL)
return -ENOMEM;
cs42l56->dev = &i2c_client->dev;
@@ -1254,40 +1224,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- if (pdata) {
- cs42l56->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l56_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev,
- "could not allocate pdata\n");
- return -ENOMEM;
- }
- if (i2c_client->dev.of_node) {
- ret = cs42l56_handle_of_data(i2c_client,
- &cs42l56->pdata);
- if (ret != 0)
- return ret;
- }
- cs42l56->pdata = *pdata;
+ 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);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
}
-
i2c_set_clientdata(i2c_client, cs42l56);
for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
@@ -1310,14 +1257,18 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- regcache_cache_bypass(cs42l56->regmap, true);
-
ret = regmap_read(cs42l56->regmap, CS42L56_CHIP_ID_1, &reg);
+ 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;
@@ -1327,23 +1278,25 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
dev_info(&i2c_client->dev, "Alpha Rev %X Metal Rev %X\n",
alpha_rev, metal_rev);
- regcache_cache_bypass(cs42l56->regmap, false);
-
if (cs42l56->pdata.ain1a_ref_cfg)
regmap_update_bits(cs42l56->regmap, CS42L56_AIN_REFCFG_ADC_MUX,
- CS42L56_AIN1A_REF_MASK, 1);
+ 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, 1);
+ 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, 1);
+ 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, 1);
+ CS42L56_AIN2B_REF_MASK,
+ CS42L56_AIN2B_REF_MASK);
if (cs42l56->pdata.micbias_lvl)
regmap_update_bits(cs42l56->regmap, CS42L56_GAIN_BIAS_CTL,
@@ -1370,10 +1323,10 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
CS42L56_ADAPT_PWR_MASK,
cs42l56->pdata.adaptive_pwr);
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l56, &cs42l56_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l56, &cs42l56_dai, 1);
if (ret < 0)
- return ret;
+ goto err_enable;
return 0;
@@ -1383,14 +1336,12 @@ err_enable:
return ret;
}
-static int cs42l56_i2c_remove(struct i2c_client *client)
+static void cs42l56_i2c_remove(struct i2c_client *client)
{
struct cs42l56_private *cs42l56 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
regulator_bulk_disable(ARRAY_SIZE(cs42l56->supplies),
cs42l56->supplies);
- return 0;
}
static const struct of_device_id cs42l56_of_match[] = {
@@ -1401,7 +1352,7 @@ MODULE_DEVICE_TABLE(of, cs42l56_of_match);
static const struct i2c_device_id cs42l56_id[] = {
- { "cs42l56", 0 },
+ { "cs42l56" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l56_id);
@@ -1409,7 +1360,6 @@ MODULE_DEVICE_TABLE(i2c, cs42l56_id);
static struct i2c_driver cs42l56_i2c_driver = {
.driver = {
.name = "cs42l56",
- .owner = THIS_MODULE,
.of_match_table = cs42l56_of_match,
},
.id_table = cs42l56_id,
diff --git a/sound/soc/codecs/cs42l56.h b/sound/soc/codecs/cs42l56.h
index 5025ec9be9b2..62a8c3cb1a01 100644
--- a/sound/soc/codecs/cs42l56.h
+++ b/sound/soc/codecs/cs42l56.h
@@ -1,14 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cs42l52.h -- CS42L56 ALSA SoC audio driver
*
* Copyright 2014 CirrusLogic, Inc.
*
* Author: Brian Austin <brian.austin@cirrus.com>
- *
- * 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 __CS42L56_H__
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 8ecedba79606..535a867f9f2a 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* cs42l73.c -- CS42L73 ALSA Soc Audio driver
*
@@ -5,33 +6,36 @@
*
* Authors: Georgi Vlaev, Nucleus Systems Ltd, <joe@nucleusys.com>
* Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>
- *
- * 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.
- *
*/
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/of_gpio.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l73.h>
+#include "cirrus_legacy.h"
#include "cs42l73.h"
+struct cs42l73_platform_data {
+ /* RST GPIO */
+ struct gpio_desc *reset_gpio;
+ unsigned int chgfreq;
+ int jack_detection;
+ unsigned int mclk_freq;
+};
+
struct sp_config {
u8 spc, mmcc, spfs;
u32 srate;
@@ -153,111 +157,18 @@ static bool cs42l73_volatile_register(struct device *dev, unsigned int reg)
static bool cs42l73_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case CS42L73_DEVID_AB:
- case CS42L73_DEVID_CD:
- case CS42L73_DEVID_E:
- case CS42L73_REVID:
- case CS42L73_PWRCTL1:
- case CS42L73_PWRCTL2:
- case CS42L73_PWRCTL3:
- case CS42L73_CPFCHC:
- case CS42L73_OLMBMSDC:
- case CS42L73_DMMCC:
- case CS42L73_XSPC:
- case CS42L73_XSPMMCC:
- case CS42L73_ASPC:
- case CS42L73_ASPMMCC:
- case CS42L73_VSPC:
- case CS42L73_VSPMMCC:
- case CS42L73_VXSPFS:
- case CS42L73_MIOPC:
- case CS42L73_ADCIPC:
- case CS42L73_MICAPREPGAAVOL:
- case CS42L73_MICBPREPGABVOL:
- case CS42L73_IPADVOL:
- case CS42L73_IPBDVOL:
- case CS42L73_PBDC:
- case CS42L73_HLADVOL:
- case CS42L73_HLBDVOL:
- case CS42L73_SPKDVOL:
- case CS42L73_ESLDVOL:
- case CS42L73_HPAAVOL:
- case CS42L73_HPBAVOL:
- case CS42L73_LOAAVOL:
- case CS42L73_LOBAVOL:
- case CS42L73_STRINV:
- case CS42L73_XSPINV:
- case CS42L73_ASPINV:
- case CS42L73_VSPINV:
- case CS42L73_LIMARATEHL:
- case CS42L73_LIMRRATEHL:
- case CS42L73_LMAXHL:
- case CS42L73_LIMARATESPK:
- case CS42L73_LIMRRATESPK:
- case CS42L73_LMAXSPK:
- case CS42L73_LIMARATEESL:
- case CS42L73_LIMRRATEESL:
- case CS42L73_LMAXESL:
- case CS42L73_ALCARATE:
- case CS42L73_ALCRRATE:
- case CS42L73_ALCMINMAX:
- case CS42L73_NGCAB:
- case CS42L73_ALCNGMC:
- case CS42L73_MIXERCTL:
- case CS42L73_HLAIPAA:
- case CS42L73_HLBIPBA:
- case CS42L73_HLAXSPAA:
- case CS42L73_HLBXSPBA:
- case CS42L73_HLAASPAA:
- case CS42L73_HLBASPBA:
- case CS42L73_HLAVSPMA:
- case CS42L73_HLBVSPMA:
- case CS42L73_XSPAIPAA:
- case CS42L73_XSPBIPBA:
- case CS42L73_XSPAXSPAA:
- case CS42L73_XSPBXSPBA:
- case CS42L73_XSPAASPAA:
- case CS42L73_XSPAASPBA:
- case CS42L73_XSPAVSPMA:
- case CS42L73_XSPBVSPMA:
- case CS42L73_ASPAIPAA:
- case CS42L73_ASPBIPBA:
- case CS42L73_ASPAXSPAA:
- case CS42L73_ASPBXSPBA:
- case CS42L73_ASPAASPAA:
- case CS42L73_ASPBASPBA:
- case CS42L73_ASPAVSPMA:
- case CS42L73_ASPBVSPMA:
- case CS42L73_VSPAIPAA:
- case CS42L73_VSPBIPBA:
- case CS42L73_VSPAXSPAA:
- case CS42L73_VSPBXSPBA:
- case CS42L73_VSPAASPAA:
- case CS42L73_VSPBASPBA:
- case CS42L73_VSPAVSPMA:
- case CS42L73_VSPBVSPMA:
- case CS42L73_MMIXCTL:
- case CS42L73_SPKMIPMA:
- case CS42L73_SPKMXSPA:
- case CS42L73_SPKMASPA:
- case CS42L73_SPKMVSPMA:
- case CS42L73_ESLMIPMA:
- case CS42L73_ESLMXSPA:
- case CS42L73_ESLMASPA:
- case CS42L73_ESLMVSPMA:
- case CS42L73_IM1:
- case CS42L73_IM2:
+ case CS42L73_DEVID_AB ... CS42L73_DEVID_E:
+ case CS42L73_REVID ... CS42L73_IM2:
return true;
default:
return false;
}
}
-static const unsigned int hpaloa_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 14, 75, TLV_DB_SCALE_ITEM(-4900, 100, 0)
+);
static DECLARE_TLV_DB_SCALE(adc_boost_tlv, 0, 2500, 0);
@@ -267,11 +178,10 @@ static DECLARE_TLV_DB_SCALE(ipd_tlv, -9600, 100, 0);
static DECLARE_TLV_DB_SCALE(micpga_tlv, -600, 50, 0);
-static const unsigned int limiter_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 3, 7, TLV_DB_SCALE_ITEM(-1200, 300, 0)
+);
static const DECLARE_TLV_DB_SCALE(attn_tlv, -6300, 100, 1);
@@ -371,12 +281,6 @@ static SOC_ENUM_SINGLE_DECL(xsp_output_mux_enum,
CS42L73_MIXERCTL, 4,
cs42l73_spo_mixer_text);
-static const struct snd_kcontrol_new vsp_output_mux =
- SOC_DAPM_ENUM("Route", vsp_output_mux_enum);
-
-static const struct snd_kcontrol_new xsp_output_mux =
- SOC_DAPM_ENUM("Route", xsp_output_mux_enum);
-
static const struct snd_kcontrol_new hp_amp_ctl =
SOC_DAPM_SINGLE("Switch", CS42L73_PWRCTL3, 0, 1, 1);
@@ -584,8 +488,8 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -600,8 +504,8 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -618,8 +522,8 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -888,7 +792,7 @@ struct cs42l73_mclk_div {
u8 mmcc;
};
-static struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
+static const struct cs42l73_mclk_div cs42l73_mclk_coeffs[] = {
/* MCLK, Sample Rate, xMMCC[5:0] */
{5644800, 11025, 0x30},
{5644800, 22050, 0x20},
@@ -938,7 +842,7 @@ struct cs42l73_mclkx_div {
u8 mclkdiv;
};
-static struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
+static const struct cs42l73_mclkx_div cs42l73_mclkx_coeffs[] = {
{5644800, 1, 0}, /* 5644800 */
{6000000, 1, 0}, /* 6000000 */
{6144000, 1, 0}, /* 6144000 */
@@ -978,8 +882,8 @@ static int cs42l73_get_mclk_coeff(int mclk, int srate)
static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
int mclkx_coeff;
u32 mclk = 0;
@@ -993,14 +897,14 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
mclk = cs42l73_mclkx_coeffs[mclkx_coeff].mclkx /
cs42l73_mclkx_coeffs[mclkx_coeff].ratio;
- dev_dbg(codec->dev, "MCLK%u %u <-> internal MCLK %u\n",
+ 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_write(codec, CS42L73_DMMCC, dmmcc);
+ snd_soc_component_write(component, CS42L73_DMMCC, dmmcc);
priv->sysclk = mclkx_coeff;
priv->mclk = mclk;
@@ -1011,8 +915,8 @@ static int cs42l73_set_mclk(struct snd_soc_dai *dai, unsigned int freq)
static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct cs42l73_private *priv = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case CS42L73_CLKID_MCLK1:
@@ -1024,7 +928,7 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
}
if ((cs42l73_set_mclk(dai, freq)) < 0) {
- dev_err(codec->dev, "Unable to set MCLK for dai %s\n",
+ dev_err(component->dev, "Unable to set MCLK for dai %s\n",
dai->name);
return -EINVAL;
}
@@ -1036,21 +940,21 @@ static int cs42l73_set_sysclk(struct snd_soc_dai *dai,
static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, CS42L73_SPC(id));
- mmcc = snd_soc_read(codec, CS42L73_MMCC(id));
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
mmcc |= CS42L73_MS_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mmcc &= ~CS42L73_MS_MASTER;
break;
@@ -1068,12 +972,12 @@ static int cs42l73_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
case SND_SOC_DAIFMT_DSP_B:
if (mmcc & CS42L73_MS_MASTER) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format in slave mode only\n");
return -EINVAL;
}
if (id == CS42L73_ASP) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PCM format is not supported on ASP port\n");
return -EINVAL;
}
@@ -1123,7 +1027,7 @@ static unsigned int cs42l73_get_xspfs_coeff(u32 rate)
return 0; /* 0 = Don't know */
}
-static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
+static void cs42l73_update_asrc(struct snd_soc_component *component, int id, int srate)
{
u8 spfs = 0;
@@ -1132,13 +1036,13 @@ static void cs42l73_update_asrc(struct snd_soc_codec *codec, int id, int srate)
switch (id) {
case CS42L73_XSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0x0f, spfs);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0x0f, spfs);
break;
case CS42L73_ASP:
- snd_soc_update_bits(codec, CS42L73_ASPC, 0x3c, spfs << 2);
+ snd_soc_component_update_bits(component, CS42L73_ASPC, 0x3c, spfs << 2);
break;
case CS42L73_VSP:
- snd_soc_update_bits(codec, CS42L73_VXSPFS, 0xf0, spfs << 4);
+ snd_soc_component_update_bits(component, CS42L73_VXSPFS, 0xf0, spfs << 4);
break;
default:
break;
@@ -1149,8 +1053,8 @@ 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_codec *codec = dai->codec;
- struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -1164,7 +1068,7 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
if (mclk_coeff < 0)
return -EINVAL;
- dev_dbg(codec->dev,
+ 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);
@@ -1185,38 +1089,38 @@ static int cs42l73_pcm_hw_params(struct snd_pcm_substream *substream,
/* Update ASRCs */
priv->config[id].srate = srate;
- snd_soc_write(codec, CS42L73_SPC(id), priv->config[id].spc);
- snd_soc_write(codec, CS42L73_MMCC(id), priv->config[id].mmcc);
+ 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(codec, id, srate);
+ cs42l73_update_asrc(component, id, srate);
return 0;
}
-static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
+static int cs42l73_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 0);
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 0);
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
regcache_cache_only(cs42l73->regmap, false);
regcache_sync(cs42l73->regmap);
}
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, CS42L73_PWRCTL1, CS42L73_PDN, 1);
+ snd_soc_component_update_bits(component, CS42L73_PWRCTL1, CS42L73_PDN, 1);
if (cs42l73->shutdwn_delay > 0) {
mdelay(cs42l73->shutdwn_delay);
cs42l73->shutdwn_delay = 0;
@@ -1225,20 +1129,19 @@ static int cs42l73_set_bias_level(struct snd_soc_codec *codec,
* down.
*/
}
- snd_soc_update_bits(codec, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
+ snd_soc_component_update_bits(component, CS42L73_DMMCC, CS42L73_MCLKDIS, 1);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
static int cs42l73_set_tristate(struct snd_soc_dai *dai, int tristate)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int id = dai->id;
- return snd_soc_update_bits(codec, CS42L73_SPC(id),
- 0x7F, tristate << 7);
+ 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 = {
@@ -1286,7 +1189,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-asp",
@@ -1306,7 +1209,7 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "cs42l73-vsp",
@@ -1326,17 +1229,17 @@ static struct snd_soc_dai_driver cs42l73_dai[] = {
.formats = CS42L73_FORMATS,
},
.ops = &cs42l73_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static int cs42l73_probe(struct snd_soc_codec *codec)
+static int cs42l73_probe(struct snd_soc_component *component)
{
- struct cs42l73_private *cs42l73 = snd_soc_codec_get_drvdata(codec);
+ struct cs42l73_private *cs42l73 = snd_soc_component_get_drvdata(component);
/* Set Charge Pump Frequency */
if (cs42l73->pdata.chgfreq)
- snd_soc_update_bits(codec, CS42L73_CPFCHC,
+ snd_soc_component_update_bits(component, CS42L73_CPFCHC,
CS42L73_CHARGEPUMP_MASK,
cs42l73->pdata.chgfreq << 4);
@@ -1347,18 +1250,19 @@ static int cs42l73_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
- .probe = cs42l73_probe,
- .set_bias_level = cs42l73_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
-
- .controls = cs42l73_snd_controls,
- .num_controls = ARRAY_SIZE(cs42l73_snd_controls),
+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 = {
@@ -1370,21 +1274,21 @@ static const struct regmap_config cs42l73_regmap = {
.num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults),
.volatile_reg = cs42l73_volatile_register,
.readable_reg = cs42l73_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
- const struct i2c_device_id *id)
+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;
- unsigned int devid = 0;
+ struct cs42l73_platform_data *pdata;
+ int ret, devid;
unsigned int reg;
u32 val32;
- cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l73_private),
- GFP_KERNEL);
+ cs42l73 = devm_kzalloc(&i2c_client->dev, sizeof(*cs42l73), GFP_KERNEL);
if (!cs42l73)
return -ENOMEM;
@@ -1395,85 +1299,66 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
return ret;
}
- if (pdata) {
- cs42l73->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev,
- sizeof(struct cs42l73_platform_data),
- GFP_KERNEL);
- if (!pdata) {
- dev_err(&i2c_client->dev, "could not allocate pdata\n");
- 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;
+ 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 = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L73 /RST");
+ 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);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
}
- regcache_cache_bypass(cs42l73->regmap, true);
-
/* initialize codec */
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
- devid = (reg & 0xFF) << 12;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_CD, &reg);
- devid |= (reg & 0xFF) << 4;
-
- ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_E, &reg);
- devid |= (reg & 0xF0) >> 4;
+ 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);
- return ret;
+ goto err_reset;
}
ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "Get Revision ID failed\n");
- return ret;;
+ goto err_reset;
}
dev_info(&i2c_client->dev,
"Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF);
- regcache_cache_bypass(cs42l73->regmap, false);
-
- ret = snd_soc_register_codec(&i2c_client->dev,
- &soc_codec_dev_cs42l73, cs42l73_dai,
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
if (ret < 0)
- return ret;
- return 0;
-}
+ goto err_reset;
-static int cs42l73_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+
+ return ret;
}
static const struct of_device_id cs42l73_of_match[] = {
@@ -1483,7 +1368,7 @@ static const struct of_device_id cs42l73_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l73_of_match);
static const struct i2c_device_id cs42l73_id[] = {
- {"cs42l73", 0},
+ {"cs42l73"},
{}
};
@@ -1492,12 +1377,10 @@ MODULE_DEVICE_TABLE(i2c, cs42l73_id);
static struct i2c_driver cs42l73_i2c_driver = {
.driver = {
.name = "cs42l73",
- .owner = THIS_MODULE,
.of_match_table = cs42l73_of_match,
},
.id_table = cs42l73_id,
.probe = cs42l73_i2c_probe,
- .remove = cs42l73_i2c_remove,
};
diff --git a/sound/soc/codecs/cs42l73.h b/sound/soc/codecs/cs42l73.h
index 45746186a678..e43a35576111 100644
--- a/sound/soc/codecs/cs42l73.h
+++ b/sound/soc/codecs/cs42l73.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC CS42L73 codec driver
*
@@ -5,21 +6,6 @@
*
* Author: Georgi Vlaev <joe@nucleusys.com>
* Brian Austin <brian.austin@cirrus.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#ifndef __CS42L73_H__
diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c
new file mode 100644
index 000000000000..53a7fe1ab3dd
--- /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 <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#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 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 = {
+ 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 = pm_ptr(&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 <povik+lin@cutebit.org>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_CS42L42_CORE");
diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c
new file mode 100644
index 000000000000..88cf3c03986e
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.c
@@ -0,0 +1,1111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs42l84.c -- CS42L84 ALSA SoC audio driver
+ *
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42{.c,.h}
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "cs42l84.h"
+#include "cirrus_legacy.h"
+
+struct cs42l84_private {
+ struct regmap *regmap;
+ struct device *dev;
+ struct gpio_desc *reset_gpio;
+ struct snd_soc_jack *jack;
+ struct mutex irq_lock;
+ u8 tip_state;
+ u8 ring_state;
+ int pll_config;
+ int bclk;
+ u8 pll_mclk_f;
+ u32 srate;
+ u8 stream_use;
+ int hs_type;
+};
+
+static bool cs42l84_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L84_DEVID ... CS42L84_DEVID+5:
+ case CS42L84_TSRS_PLUG_INT_STATUS:
+ case CS42L84_PLL_LOCK_STATUS:
+ case CS42L84_TSRS_PLUG_STATUS:
+ case CS42L84_HS_DET_STATUS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs42l84_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+
+ .volatile_reg = cs42l84_volatile_register,
+
+ .max_register = 0x73fe,
+
+ .cache_type = REGCACHE_MAPLE,
+
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int cs42l84_put_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret, ret2, updated = 0;
+
+ vola = val->value.integer.value[0] + mc->min;
+ volb = val->value.integer.value[1] + mc->min;
+
+ if (vola < mc->min || vola > mc->max || volb < mc->min || volb > mc->max)
+ return -EINVAL;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE,
+ CS42L84_FRZ_CTL_ENGAGE);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_LSB,
+ 0xff, vola & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHA_VOL_MSB,
+ 0xff, (vola >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_LSB,
+ 0xff, volb & 0xff);
+ if (ret < 0)
+ goto bail;
+ updated |= ret;
+
+ ret = snd_soc_component_update_bits(component, CS42L84_DAC_CHB_VOL_MSB,
+ 0xff, (volb >> 8) & 0x01);
+ if (ret < 0)
+ goto bail;
+ ret |= updated;
+
+bail:
+ ret2 = snd_soc_component_update_bits(component, CS42L84_FRZ_CTL,
+ CS42L84_FRZ_CTL_ENGAGE, 0);
+ if (ret2 < 0 && ret >= 0)
+ ret = ret2;
+
+ return ret;
+}
+
+static int cs42l84_get_dac_vol(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *val)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kctl);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *) kctl->private_value;
+ int vola, volb;
+ int ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ vola = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHA_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ vola |= (ret & 1) << 8;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_LSB);
+ if (ret < 0)
+ return ret;
+ volb = ret;
+
+ ret = snd_soc_component_read(component, CS42L84_DAC_CHB_VOL_MSB);
+ if (ret < 0)
+ return ret;
+ volb |= (ret & 1) << 8;
+
+ if (vola & BIT(8))
+ vola |= ~((int)(BIT(8) - 1));
+ if (volb & BIT(8))
+ volb |= ~((int)(BIT(8) - 1));
+
+ val->value.integer.value[0] = vola - mc->min;
+ val->value.integer.value[1] = volb - mc->min;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(cs42l84_dac_tlv, -12800, 50, true);
+static const DECLARE_TLV_DB_SCALE(cs42l84_adc_tlv, -1200, 50, false);
+static const DECLARE_TLV_DB_SCALE(cs42l84_pre_tlv, 0, 1000, false);
+
+static const struct snd_kcontrol_new cs42l84_snd_controls[] = {
+ SOC_DOUBLE_R_S_EXT_TLV("DAC Playback Volume", CS42L84_DAC_CHA_VOL_LSB,
+ CS42L84_DAC_CHB_VOL_LSB, 0, -256, 24, 8, 0,
+ cs42l84_get_dac_vol, cs42l84_put_dac_vol, cs42l84_dac_tlv),
+ SOC_SINGLE_TLV("ADC Preamp Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT, 2, 0, cs42l84_pre_tlv),
+ SOC_SINGLE_TLV("ADC PGA Capture Volume", CS42L84_ADC_CTL1,
+ CS42L84_ADC_CTL1_PGA_GAIN_SHIFT, 24, 0, cs42l84_adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("WNF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_WNF_CF_SHIFT, 3, 0),
+ SOC_SINGLE("ADC HPF Switch", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("HPF Corner Frequency", CS42L84_ADC_CTL4,
+ CS42L84_ADC_CTL4_HPF_CF_SHIFT, 3, 0),
+};
+
+static const char * const cs42l84_mux_text[] = {
+ "Blank", "ADC", "ASP RX CH1", "ASP RX CH2",
+};
+
+static const unsigned int cs42l84_mux_values[] = {
+ 0b0000, 0b0111, 0b1101, 0b1110,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_daca_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACA_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_dacb_mux_enum,
+ CS42L84_BUS_DAC_SRC, CS42L84_BUS_DAC_SRC_DACB_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l84_sdout1_mux_enum,
+ CS42L84_BUS_ASP_TX_SRC, CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT,
+ 0b1111, cs42l84_mux_text, cs42l84_mux_values);
+
+static const struct snd_kcontrol_new cs42l84_daca_mux_ctrl =
+ SOC_DAPM_ENUM("DACA Select", cs42l84_daca_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_dacb_mux_ctrl =
+ SOC_DAPM_ENUM("DACB Select", cs42l84_dacb_mux_enum);
+
+static const struct snd_kcontrol_new cs42l84_sdout1_mux_ctrl =
+ SOC_DAPM_ENUM("SDOUT1 Select", cs42l84_sdout1_mux_enum);
+
+static const struct snd_soc_dapm_widget cs42l84_dapm_widgets[] = {
+ /* Playback Path */
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_DAC("DAC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_DAC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("DACA Select", SND_SOC_NOPM, 0, 0, &cs42l84_daca_mux_ctrl),
+ SND_SOC_DAPM_MUX("DACB Select", SND_SOC_NOPM, 0, 0, &cs42l84_dacb_mux_ctrl),
+ SND_SOC_DAPM_AIF_IN("SDIN1", NULL, 0, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH1_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("SDIN2", NULL, 1, CS42L84_ASP_RX_EN, CS42L84_ASP_RX_EN_CH2_SHIFT, 0),
+
+ /* Capture Path */
+ SND_SOC_DAPM_INPUT("HS"),
+ SND_SOC_DAPM_ADC("ADC", NULL, CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ADC_SHIFT, 0),
+ SND_SOC_DAPM_MUX("SDOUT1 Select", SND_SOC_NOPM, 0, 0, &cs42l84_sdout1_mux_ctrl),
+ SND_SOC_DAPM_AIF_OUT("SDOUT1", NULL, 0, CS42L84_ASP_TX_EN, CS42L84_ASP_TX_EN_CH1_SHIFT, 0),
+
+ /* Playback/Capture Requirements */
+ SND_SOC_DAPM_SUPPLY("BUS", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_BUS_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASP", CS42L84_MSM_BLOCK_EN2, CS42L84_MSM_BLOCK_EN2_ASP_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("BCLK", CS42L84_ASP_CTL, CS42L84_ASP_CTL_BCLK_EN_SHIFT, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route cs42l84_audio_map[] = {
+ /* Playback Path */
+ {"HP", NULL, "DAC"},
+ {"DAC", NULL, "DACA Select"},
+ {"DAC", NULL, "DACB Select"},
+ {"DACA Select", "ASP RX CH1", "SDIN1"},
+ {"DACA Select", "ASP RX CH2", "SDIN2"},
+ {"DACB Select", "ASP RX CH1", "SDIN1"},
+ {"DACB Select", "ASP RX CH2", "SDIN2"},
+ {"SDIN1", NULL, "Playback"},
+ {"SDIN2", NULL, "Playback"},
+
+ {"ADC", NULL, "HS"},
+ {"SDOUT1 Select", "ADC", "ADC"},
+ {"SDOUT1", NULL, "SDOUT1 Select"},
+ {"Capture", NULL, "SDOUT1"},
+
+ /* Playback Requirements */
+ {"DAC", NULL, "BUS"},
+ {"SDIN1", NULL, "ASP"},
+ {"SDIN2", NULL, "ASP"},
+ {"SDIN1", NULL, "BCLK"},
+ {"SDIN2", NULL, "BCLK"},
+
+ /* Capture Requirements */
+ {"SDOUT1", NULL, "BUS"},
+ {"SDOUT1", NULL, "ASP"},
+ {"SDOUT1", NULL, "BCLK"},
+};
+
+static int cs42l84_set_jack(struct snd_soc_component *component, struct snd_soc_jack *jk, void *d)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+
+ /* Prevent race with interrupt handler */
+ mutex_lock(&cs42l84->irq_lock);
+ cs42l84->jack = jk;
+ snd_soc_jack_report(jk, cs42l84->hs_type, SND_JACK_HEADSET);
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return 0;
+}
+
+static int cs42l84_component_probe(struct snd_soc_component *component)
+{
+ snd_soc_component_update_bits(component, CS42L84_ASP_CTL,
+ CS42L84_ASP_CTL_TDM_MODE, 0);
+ snd_soc_component_update_bits(component, CS42L84_HP_VOL_CTL,
+ CS42L84_HP_VOL_CTL_SOFT | CS42L84_HP_VOL_CTL_ZERO_CROSS,
+ CS42L84_HP_VOL_CTL_ZERO_CROSS);
+
+ /* TDM settings */
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE |
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_RX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH1_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL1,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE | \
+ CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB,
+ CS42L84_ASP_RX_CHx_CTL1_EDGE);
+ snd_soc_component_update_bits(component, CS42L84_ASP_TX_CH2_CTL2,
+ CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB, 0);
+ /* Routing defaults */
+ snd_soc_component_write(component, CS42L84_BUS_DAC_SRC,
+ 0b1101 << CS42L84_BUS_DAC_SRC_DACA_SHIFT |
+ 0b1110 << CS42L84_BUS_DAC_SRC_DACB_SHIFT);
+ snd_soc_component_write(component, CS42L84_BUS_ASP_TX_SRC,
+ 0b0111 << CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs42l84 = {
+ .set_jack = cs42l84_set_jack,
+ .probe = cs42l84_component_probe,
+ .controls = cs42l84_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l84_snd_controls),
+ .dapm_widgets = cs42l84_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l84_dapm_widgets),
+ .dapm_routes = cs42l84_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l84_audio_map),
+ .endianness = 1,
+};
+
+struct cs42l84_pll_params {
+ u32 bclk;
+ u8 mclk_src_sel;
+ u8 bclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+};
+
+/*
+ * Common PLL Settings for given BCLK
+ */
+static const struct cs42l84_pll_params pll_ratio_table[] = {
+ { 3072000, 1, 0, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 6144000, 1, 1, 0x40, 0x000000, 0x03, 0x10, 12288000},
+ { 12288000, 0, 0, 0, 0, 0, 0, 12288000},
+ { 24576000, 1, 3, 0x40, 0x000000, 0x03, 0x10, 12288000},
+};
+
+static int cs42l84_pll_config(struct snd_soc_component *component)
+{
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+ u32 clk;
+ u32 fsync;
+
+ clk = cs42l84->bclk;
+
+ /* Don't reconfigure if there is an audio stream running */
+ if (cs42l84->stream_use) {
+ if (pll_ratio_table[cs42l84->pll_config].bclk == clk)
+ return 0;
+ else
+ return -EBUSY;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == clk) {
+ cs42l84->pll_config = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(pll_ratio_table))
+ return -EINVAL;
+
+ /* Set up the LRCLK */
+ fsync = clk / cs42l84->srate;
+ if (((fsync * cs42l84->srate) != clk)
+ || ((fsync % 2) != 0)) {
+ dev_err(component->dev,
+ "Unsupported bclk %d/sample rate %d\n",
+ clk, cs42l84->srate);
+ return -EINVAL;
+ }
+
+ /* Set the LRCLK period */
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL2,
+ CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO, fsync & 0x7f));
+ snd_soc_component_update_bits(component, CS42L84_ASP_FSYNC_CTL3,
+ CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI,
+ FIELD_PREP(CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI, fsync >> 7));
+
+ /* Save what the MCLK will be */
+ switch (pll_ratio_table[i].mclk_int) {
+ case 12000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12MHZ;
+ break;
+ case 12288000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_12_288KHZ;
+ break;
+ case 24000000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24MHZ;
+ break;
+ case 24576000:
+ cs42l84->pll_mclk_f = CS42L84_CCM_CTL1_MCLK_F_24_576KHZ;
+ break;
+ }
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_EN, 0);
+
+ if (pll_ratio_table[i].mclk_src_sel) {
+ /* Configure PLL */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL3, CS42L84_CCM_CTL3_REFCLK_DIV,
+ FIELD_PREP(CS42L84_CCM_CTL3_REFCLK_DIV, pll_ratio_table[i].bclk_prediv));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_INT,
+ pll_ratio_table[i].pll_div_int);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC0,
+ pll_ratio_table[i].pll_div_frac);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC1,
+ pll_ratio_table[i].pll_div_frac >> 8);
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIV_FRAC2,
+ pll_ratio_table[i].pll_div_frac >> 16);
+ snd_soc_component_update_bits(component,
+ CS42L84_PLL_CTL1, CS42L84_PLL_CTL1_MODE,
+ FIELD_PREP(CS42L84_PLL_CTL1_MODE, pll_ratio_table[i].pll_mode));
+ snd_soc_component_write(component,
+ CS42L84_PLL_DIVOUT,
+ pll_ratio_table[i].pll_divout);
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs42l84_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 cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int ret;
+ u32 ccm_samp_rate;
+
+ cs42l84->srate = params_rate(params);
+
+ ret = cs42l84_pll_config(component);
+ if (ret)
+ return ret;
+
+ switch (params_rate(params)) {
+ case 44100:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_44K1HZ;
+ break;
+ case 48000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_48KHZ;
+ break;
+ case 88200:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_88K2HZ;
+ break;
+ case 96000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_96KHZ;
+ break;
+ case 176400:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_176K4HZ;
+ break;
+ case 192000:
+ ccm_samp_rate = CS42L84_CCM_SAMP_RATE_RATE_192KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, CS42L84_CCM_SAMP_RATE, ccm_samp_rate);
+
+ switch (substream->stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_RX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+
+ case SNDRV_PCM_STREAM_CAPTURE:
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH1_WIDTH,
+ params_width(params) - 1);
+ snd_soc_component_write(component, CS42L84_ASP_TX_CH2_WIDTH,
+ params_width(params) - 1);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l84_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ if (freq == 0) {
+ cs42l84->bclk = 0;
+ return 0;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].bclk == freq) {
+ cs42l84->bclk = freq;
+ return 0;
+ }
+ }
+
+ dev_err(component->dev, "BCLK %u not supported\n", freq);
+
+ return -EINVAL;
+}
+
+static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l84_private *cs42l84 = 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, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE, 0);
+ cs42l84->stream_use &= ~(1 << stream);
+ if (!cs42l84->stream_use) {
+ /* Must disconnect PLL before stopping it */
+ snd_soc_component_write(component, CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_RCO);
+
+ usleep_range(150, 300);
+
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN, 0);
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN, 0);
+ }
+ } else {
+ if (!cs42l84->stream_use) {
+ /* SCLK must be running before codec unmute.
+ *
+ * Note carried over from CS42L42:
+ *
+ * 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.
+ */
+
+ snd_soc_component_update_bits(component, CS42L84_CCM_CTL4,
+ CS42L84_CCM_CTL4_REFCLK_EN,
+ CS42L84_CCM_CTL4_REFCLK_EN);
+
+ if (pll_ratio_table[cs42l84->pll_config].mclk_src_sel) {
+ snd_soc_component_update_bits(component, CS42L84_PLL_CTL1,
+ CS42L84_PLL_CTL1_EN,
+ CS42L84_PLL_CTL1_EN);
+ /* TODO: should we be doing something with divout here? */
+
+ ret = regmap_read_poll_timeout(cs42l84->regmap,
+ CS42L84_PLL_LOCK_STATUS,
+ regval,
+ (regval & CS42L84_PLL_LOCK_STATUS_LOCKED),
+ CS42L84_PLL_LOCK_POLL_US,
+ CS42L84_PLL_LOCK_TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(component->dev, "PLL failed to lock: %d\n", ret);
+
+ if (regval & CS42L84_PLL_LOCK_STATUS_ERROR)
+ dev_warn(component->dev, "PLL lock error\n");
+
+ /* PLL must be running to drive glitchless switch logic */
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_PLL)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ } else {
+ snd_soc_component_update_bits(component,
+ CS42L84_CCM_CTL1,
+ CS42L84_CCM_CTL1_MCLK_SRC | CS42L84_CCM_CTL1_MCLK_FREQ,
+ FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_BCLK)
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, cs42l84->pll_mclk_f));
+ usleep_range(CS42L84_CLOCK_SWITCH_DELAY_US, CS42L84_CLOCK_SWITCH_DELAY_US*2);
+ }
+ }
+ cs42l84->stream_use |= 1 << stream;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ /* Un-mute the headphone */
+ snd_soc_component_update_bits(component, CS42L84_DAC_CTL1,
+ CS42L84_DAC_CTL1_UNMUTE,
+ CS42L84_DAC_CTL1_UNMUTE);
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l84_ops = {
+ .hw_params = cs42l84_pcm_hw_params,
+ .set_fmt = cs42l84_set_dai_fmt,
+ .set_sysclk = cs42l84_set_sysclk,
+ .mute_stream = cs42l84_mute_stream,
+};
+
+#define CS42L84_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver cs42l84_dai = {
+ .name = "cs42l84",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = CS42L84_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ .ops = &cs42l84_ops,
+};
+
+struct cs42l84_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l84_irq_params irq_params_table[] = {
+ {CS42L84_TSRS_PLUG_INT_STATUS, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_TSRS_PLUG_VAL_MASK}
+};
+
+static void cs42l84_detect_hs(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Power up HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 3) | /* 2.7 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Power up level detection circuitry */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET, 0);
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ /* Connect HSBIAS in CTIA wiring */
+ /* TODO: Should likely be subject of detection */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 0));
+
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 3));
+
+ /* TODO: Optimize */
+ msleep(50);
+
+ regmap_read(cs42l84->regmap, CS42L84_HS_DET_STATUS2, &reg);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET,
+ CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET);
+
+ switch (reg & 0b11) {
+ case 0b11: /* shorted */
+ case 0b00: /* open */
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1)); /* 0.0 V */
+ break;
+ }
+
+ switch (reg & 0b11) {
+ case 0b10: /* load */
+ dev_dbg(cs42l84->dev, "Detected mic\n");
+ cs42l84->hs_type = SND_JACK_HEADSET;
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADSET,
+ SND_JACK_HEADSET);
+ break;
+
+ case 0b00: /* open */
+ dev_dbg(cs42l84->dev, "Detected open circuit on HS4\n");
+ fallthrough;
+ case 0b11: /* shorted */
+ default:
+ snd_soc_jack_report(cs42l84->jack, SND_JACK_HEADPHONE,
+ SND_JACK_HEADSET);
+ cs42l84->hs_type = SND_JACK_HEADPHONE;
+ dev_dbg(cs42l84->dev, "Detected bare headphone (no mic)\n");
+ break;
+ }
+}
+
+static void cs42l84_revert_hs(struct cs42l84_private *cs42l84)
+{
+ /* Power down HSBIAS */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MISC_DET_CTL,
+ CS42L84_MISC_DET_CTL_HSBIAS_CTL | CS42L84_MISC_DET_CTL_DETECT_MODE,
+ FIELD_PREP(CS42L84_MISC_DET_CTL_HSBIAS_CTL, 1) | /* 0.0 V */
+ FIELD_PREP(CS42L84_MISC_DET_CTL_DETECT_MODE, 0));
+
+ /* Disconnect HSBIAS */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2));
+}
+
+static void cs42l84_set_interrupt_masks(struct cs42l84_private *cs42l84,
+ unsigned int val)
+{
+ regmap_update_bits(cs42l84->regmap, CS42L84_TSRS_PLUG_INT_MASK,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG |
+ CS42L84_TS_PLUG | CS42L84_TS_UNPLUG,
+ val);
+}
+
+static irqreturn_t cs42l84_irq_thread(int irq, void *data)
+{
+ struct cs42l84_private *cs42l84 = (struct cs42l84_private *)data;
+ unsigned int stickies[1];
+ unsigned int masks[1];
+ unsigned int reg;
+ u8 current_tip_state;
+ u8 current_ring_state;
+ int i;
+
+ mutex_lock(&cs42l84->irq_lock);
+ /* Read sticky registers to clear interrupt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l84->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l84->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* When handling plug sene IRQs, we only care about EITHER tip OR ring.
+ * Ring is useless on remove, and is only useful on insert for
+ * detecting if the plug state has changed AFTER we have handled the
+ * tip sense IRQ, e.g. if the plug was not fully seated within the tip
+ * sense debounce time.
+ */
+
+ if ((~masks[0]) & irq_params_table[0].mask) {
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ if (current_tip_state != cs42l84->tip_state) {
+ cs42l84->tip_state = current_tip_state;
+ switch (current_tip_state) {
+ case CS42L84_PLUG:
+ dev_dbg(cs42l84->dev, "Plug event\n");
+
+ cs42l84_detect_hs(cs42l84);
+
+ /*
+ * Check the tip sense status again, and possibly invalidate
+ * the detection result
+ *
+ * Thanks to debounce, this should reliably indicate if the tip
+ * was disconnected at any point during the detection procedure.
+ */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ current_tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+ if (current_tip_state != CS42L84_PLUG) {
+ dev_dbg(cs42l84->dev, "Wobbly connection, detection invalidated\n");
+ cs42l84->tip_state = CS42L84_UNPLUG;
+ cs42l84_revert_hs(cs42l84);
+ }
+
+ /* Unmask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, 0);
+ break;
+ case CS42L84_UNPLUG:
+ cs42l84->ring_state = CS42L84_UNPLUG;
+ dev_dbg(cs42l84->dev, "Unplug event\n");
+
+ cs42l84_revert_hs(cs42l84);
+ cs42l84->hs_type = 0;
+ snd_soc_jack_report(cs42l84->jack, 0,
+ SND_JACK_HEADSET);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84,
+ CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+ break;
+ default:
+ cs42l84->ring_state = CS42L84_TRANS;
+ break;
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+ }
+
+ /* Tip state didn't change, we must've got a ring sense IRQ */
+ current_ring_state = (((char) reg) &
+ (CS42L84_RS_PLUG | CS42L84_RS_UNPLUG)) >>
+ CS42L84_RS_PLUG_SHIFT;
+
+ if (current_ring_state != cs42l84->ring_state) {
+ cs42l84->ring_state = current_ring_state;
+ if (current_ring_state == CS42L84_PLUG)
+ cs42l84_detect_hs(cs42l84);
+ }
+ }
+
+ mutex_unlock(&cs42l84->irq_lock);
+
+ return IRQ_HANDLED;
+}
+
+static void cs42l84_setup_plug_detect(struct cs42l84_private *cs42l84)
+{
+ unsigned int reg;
+
+ /* Set up plug detection */
+ regmap_update_bits(cs42l84->regmap, CS42L84_MIC_DET_CTL4,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP,
+ CS42L84_MIC_DET_CTL4_LATCH_TO_VP);
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL2,
+ CS42L84_TIP_SENSE_CTL2_MODE,
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL2_MODE, CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET));
+ regmap_update_bits(cs42l84->regmap, CS42L84_RING_SENSE_CTL,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ CS42L84_RING_SENSE_CTL_RISETIME | CS42L84_RING_SENSE_CTL_FALLTIME,
+ CS42L84_RING_SENSE_CTL_INV | CS42L84_RING_SENSE_CTL_UNK1 |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_125MS) |
+ FIELD_PREP(CS42L84_RING_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_TIP_SENSE_CTL,
+ CS42L84_TIP_SENSE_CTL_INV |
+ CS42L84_TIP_SENSE_CTL_RISETIME | CS42L84_TIP_SENSE_CTL_FALLTIME,
+ CS42L84_TIP_SENSE_CTL_INV |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_RISETIME, CS42L84_DEBOUNCE_TIME_500MS) |
+ FIELD_PREP(CS42L84_TIP_SENSE_CTL_FALLTIME, CS42L84_DEBOUNCE_TIME_125MS));
+ regmap_update_bits(cs42l84->regmap, CS42L84_MSM_BLOCK_EN3,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE,
+ CS42L84_MSM_BLOCK_EN3_TR_SENSE);
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l84->regmap, CS42L84_TSRS_PLUG_STATUS, &reg);
+ cs42l84->tip_state = (((char) reg) &
+ (CS42L84_TS_PLUG | CS42L84_TS_UNPLUG)) >>
+ CS42L84_TS_PLUG_SHIFT;
+
+ /* Set mic-detection threshold */
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_MIC_DET_CTL1, CS42L84_MIC_DET_CTL1_HS_DET_LEVEL,
+ FIELD_PREP(CS42L84_MIC_DET_CTL1_HS_DET_LEVEL, 0x2c)); /* ~1.9 V */
+
+ /* Disconnect HSBIAS (initially) */
+ regmap_write(cs42l84->regmap,
+ CS42L84_HS_SWITCH_CTL,
+ CS42L84_HS_SWITCH_CTL_REF_HS3 | \
+ CS42L84_HS_SWITCH_CTL_REF_HS4 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 | \
+ CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS3 | \
+ CS42L84_HS_SWITCH_CTL_GNDHS_HS4);
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_DET_CTL2,
+ CS42L84_HS_DET_CTL2_SET | CS42L84_HS_DET_CTL2_CTL,
+ FIELD_PREP(CS42L84_HS_DET_CTL2_SET, 2) |
+ FIELD_PREP(CS42L84_HS_DET_CTL2_CTL, 0));
+ regmap_update_bits(cs42l84->regmap,
+ CS42L84_HS_CLAMP_DISABLE, 1, 1);
+
+}
+
+static int cs42l84_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84;
+ int ret, devid;
+ unsigned int reg;
+
+ cs42l84 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l84_private),
+ GFP_KERNEL);
+ if (!cs42l84)
+ return -ENOMEM;
+
+ cs42l84->dev = &i2c_client->dev;
+ i2c_set_clientdata(i2c_client, cs42l84);
+ mutex_init(&cs42l84->irq_lock);
+
+ cs42l84->regmap = devm_regmap_init_i2c(i2c_client, &cs42l84_regmap);
+ if (IS_ERR(cs42l84->regmap)) {
+ ret = PTR_ERR(cs42l84->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l84->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l84->reset_gpio)) {
+ ret = PTR_ERR(cs42l84->reset_gpio);
+ goto err_disable_noreset;
+ }
+
+ if (cs42l84->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 1);
+ }
+ usleep_range(CS42L84_BOOT_TIME_US, CS42L84_BOOT_TIME_US * 2);
+
+ /* Request IRQ if one was specified */
+ if (i2c_client->irq) {
+ ret = request_threaded_irq(i2c_client->irq,
+ NULL, cs42l84_irq_thread,
+ IRQF_ONESHOT,
+ "cs42l84", cs42l84);
+ if (ret == -EPROBE_DEFER) {
+ goto err_disable_noirq;
+ } else if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request IRQ: %d\n", ret);
+ goto err_disable_noirq;
+ }
+ }
+
+ /* initialize codec */
+ devid = cirrus_read_device_id(cs42l84->regmap, CS42L84_DEVID);
+ if (devid < 0) {
+ ret = devid;
+ dev_err(&i2c_client->dev, "Failed to read device ID: %d\n", ret);
+ goto err_disable;
+ }
+
+ if (devid != CS42L84_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS42L84 Device ID (%X). Expected %X\n",
+ devid, CS42L84_CHIP_ID);
+ ret = -EINVAL;
+ goto err_disable;
+ }
+
+ ret = regmap_read(cs42l84->regmap, CS42L84_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_shutdown;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS42L84, Revision: %02X\n", reg & 0xFF);
+
+ /* Setup plug detection */
+ cs42l84_setup_plug_detect(cs42l84);
+
+ /* Mask ring sense interrupts */
+ cs42l84_set_interrupt_masks(cs42l84, CS42L84_RS_PLUG | CS42L84_RS_UNPLUG);
+
+ /* Register codec for machine driver */
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_component_dev_cs42l84, &cs42l84_dai, 1);
+ if (ret < 0)
+ goto err_shutdown;
+
+ return 0;
+
+err_shutdown:
+ /* Nothing to do */
+
+err_disable:
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+err_disable_noirq:
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+err_disable_noreset:
+ return ret;
+}
+
+static void cs42l84_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l84_private *cs42l84 = i2c_get_clientdata(i2c_client);
+
+ if (i2c_client->irq)
+ free_irq(i2c_client->irq, cs42l84);
+
+ gpiod_set_value_cansleep(cs42l84->reset_gpio, 0);
+}
+
+static const struct of_device_id cs42l84_of_match[] = {
+ { .compatible = "cirrus,cs42l84", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs42l84_of_match);
+
+static const struct i2c_device_id cs42l84_id[] = {
+ { "cs42l84" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs42l84_id);
+
+static struct i2c_driver cs42l84_i2c_driver = {
+ .driver = {
+ .name = "cs42l84",
+ .of_match_table = cs42l84_of_match,
+ },
+ .id_table = cs42l84_id,
+ .probe = cs42l84_i2c_probe,
+ .remove = cs42l84_i2c_remove,
+};
+
+module_i2c_driver(cs42l84_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L84 driver");
+MODULE_AUTHOR("Martin Povišer <povik+lin@cutebit.org>");
+MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");
+MODULE_AUTHOR("James Calligeros <jcalligeros99@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l84.h b/sound/soc/codecs/cs42l84.h
new file mode 100644
index 000000000000..dbf778a902b9
--- /dev/null
+++ b/sound/soc/codecs/cs42l84.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) The Asahi Linux Contributors
+ *
+ * Based on sound/soc/codecs/cs42l42.h
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ */
+
+
+#ifndef __CS42L84_H__
+#define __CS42L84_H__
+
+#include <linux/bits.h>
+
+#define CS42L84_CHIP_ID 0x42a84
+
+#define CS42L84_DEVID 0x0000
+#define CS42L84_REVID 0x73fe
+#define CS42L84_FRZ_CTL 0x0006
+#define CS42L84_FRZ_CTL_ENGAGE BIT(0)
+
+#define CS42L84_TSRS_PLUG_INT_STATUS 0x0400
+#define CS42L84_TSRS_PLUG_INT_MASK 0x0418
+#define CS42L84_RS_PLUG_SHIFT 0
+#define CS42L84_RS_PLUG BIT(0)
+#define CS42L84_RS_UNPLUG BIT(1)
+#define CS42L84_TS_PLUG_SHIFT 2
+#define CS42L84_TS_PLUG BIT(2)
+#define CS42L84_TS_UNPLUG BIT(3)
+#define CS42L84_TSRS_PLUG_VAL_MASK GENMASK(3, 0)
+#define CS42L84_PLL_LOCK_STATUS 0x040e // probably bit 0x10
+#define CS42L84_PLL_LOCK_STATUS_LOCKED BIT(4)
+#define CS42L84_PLL_LOCK_STATUS_ERROR BIT(5)
+
+#define CS42L84_PLUG 3
+#define CS42L84_UNPLUG 0
+#define CS42L84_TRANS 1
+
+#define CS42L84_CCM_CTL1 0x0600
+#define CS42L84_CCM_CTL1_MCLK_SRC GENMASK(1, 0)
+#define CS42L84_CCM_CTL1_MCLK_SRC_RCO 0
+#define CS42L84_CCM_CTL1_MCLK_SRC_MCLK 1
+#define CS42L84_CCM_CTL1_MCLK_SRC_BCLK 2
+#define CS42L84_CCM_CTL1_MCLK_SRC_PLL 3
+#define CS42L84_CCM_CTL1_MCLK_FREQ GENMASK(3, 2)
+#define CS42L84_CCM_CTL1_MCLK_F_12MHZ 0b00
+#define CS42L84_CCM_CTL1_MCLK_F_24MHZ 0b01
+#define CS42L84_CCM_CTL1_MCLK_F_12_288KHZ 0b10
+#define CS42L84_CCM_CTL1_MCLK_F_24_576KHZ 0b11
+#define CS42L84_CCM_CTL1_RCO \
+ (FIELD_PREP(CS42L84_CCM_CTL1_MCLK_SRC, CS42L84_CCM_CTL1_MCLK_SRC_RCO) \
+ | FIELD_PREP(CS42L84_CCM_CTL1_MCLK_FREQ, CS42L84_CCM_CTL1_MCLK_F_12MHZ))
+
+#define CS42L84_CCM_SAMP_RATE 0x0601
+#define CS42L84_CCM_SAMP_RATE_RATE_48KHZ 4
+#define CS42L84_CCM_SAMP_RATE_RATE_96KHZ 5
+#define CS42L84_CCM_SAMP_RATE_RATE_192KHZ 6
+#define CS42L84_CCM_SAMP_RATE_RATE_44K1HZ 12
+#define CS42L84_CCM_SAMP_RATE_RATE_88K2HZ 13
+#define CS42L84_CCM_SAMP_RATE_RATE_176K4HZ 14
+#define CS42L84_CCM_CTL3 0x0602
+#define CS42L84_CCM_CTL3_REFCLK_DIV GENMASK(2, 1)
+#define CS42L84_CCM_CTL4 0x0603
+#define CS42L84_CCM_CTL4_REFCLK_EN BIT(0)
+
+#define CS42L84_CCM_ASP_CLK_CTRL 0x0608
+
+#define CS42L84_PLL_CTL1 0x0800
+#define CS42L84_PLL_CTL1_EN BIT(0)
+#define CS42L84_PLL_CTL1_MODE GENMASK(2, 1)
+#define CS42L84_PLL_DIV_FRAC0 0x0804
+#define CS42L84_PLL_DIV_FRAC1 0x0805
+#define CS42L84_PLL_DIV_FRAC2 0x0806
+#define CS42L84_PLL_DIV_INT 0x0807
+#define CS42L84_PLL_DIVOUT 0x0808
+
+#define CS42L84_RING_SENSE_CTL 0x1282
+#define CS42L84_RING_SENSE_CTL_INV BIT(7)
+#define CS42L84_RING_SENSE_CTL_UNK1 BIT(6)
+#define CS42L84_RING_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_RING_SENSE_CTL_RISETIME GENMASK(2, 0)
+#define CS42L84_TIP_SENSE_CTL 0x1283
+#define CS42L84_TIP_SENSE_CTL_INV BIT(7)
+#define CS42L84_TIP_SENSE_CTL_FALLTIME GENMASK(5, 3)
+#define CS42L84_TIP_SENSE_CTL_RISETIME GENMASK(2, 0)
+
+#define CS42L84_TSRS_PLUG_STATUS 0x1288
+
+#define CS42L84_TIP_SENSE_CTL2 0x1473
+#define CS42L84_TIP_SENSE_CTL2_MODE GENMASK(7, 6)
+#define CS42L84_TIP_SENSE_CTL2_MODE_DISABLED 0b00
+#define CS42L84_TIP_SENSE_CTL2_MODE_DIG_INPUT 0b01
+#define CS42L84_TIP_SENSE_CTL2_MODE_SHORT_DET 0b11
+#define CS42L84_TIP_SENSE_CTL2_INV BIT(5)
+
+#define CS42L84_MISC_DET_CTL 0x1474
+#define CS42L84_MISC_DET_CTL_DETECT_MODE GENMASK(4, 3)
+#define CS42L84_MISC_DET_CTL_HSBIAS_CTL GENMASK(2, 1)
+#define CS42L84_MISC_DET_CTL_PDN_MIC_LVL_DET BIT(0)
+
+#define CS42L84_MIC_DET_CTL1 0x1475
+#define CS42L84_MIC_DET_CTL1_HS_DET_LEVEL GENMASK(5, 0)
+
+#define CS42L84_MIC_DET_CTL4 0x1477
+#define CS42L84_MIC_DET_CTL4_LATCH_TO_VP BIT(1)
+
+#define CS42L84_HS_DET_STATUS2 0x147d
+
+#define CS42L84_MSM_BLOCK_EN1 0x1800
+#define CS42L84_MSM_BLOCK_EN2 0x1801
+#define CS42L84_MSM_BLOCK_EN2_ASP_SHIFT 6
+#define CS42L84_MSM_BLOCK_EN2_BUS_SHIFT 5
+#define CS42L84_MSM_BLOCK_EN2_DAC_SHIFT 4
+#define CS42L84_MSM_BLOCK_EN2_ADC_SHIFT 3
+#define CS42L84_MSM_BLOCK_EN3 0x1802
+#define CS42L84_MSM_BLOCK_EN3_TR_SENSE BIT(3)
+
+#define CS42L84_HS_DET_CTL2 0x1811
+#define CS42L84_HS_DET_CTL2_CTL GENMASK(7, 6)
+#define CS42L84_HS_DET_CTL2_SET GENMASK(5, 4)
+#define CS42L84_HS_DET_CTL2_REF BIT(3)
+#define CS42L84_HS_DET_CTL2_AUTO_TIME GENMASK(1, 0)
+
+#define CS42L84_HS_SWITCH_CTL 0x1812
+#define CS42L84_HS_SWITCH_CTL_REF_HS3 BIT(7)
+#define CS42L84_HS_SWITCH_CTL_REF_HS4 BIT(6)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS3 BIT(5)
+#define CS42L84_HS_SWITCH_CTL_HSB_FILT_HS4 BIT(4)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS3 BIT(3)
+#define CS42L84_HS_SWITCH_CTL_HSB_HS4 BIT(2)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS3 BIT(1)
+#define CS42L84_HS_SWITCH_CTL_GNDHS_HS4 BIT(0)
+
+#define CS42L84_HS_CLAMP_DISABLE 0x1813
+
+#define CS42L84_ADC_CTL1 0x2000
+#define CS42L84_ADC_CTL1_PREAMP_GAIN_SHIFT 6
+#define CS42L84_ADC_CTL1_PGA_GAIN_SHIFT 0
+#define CS42L84_ADC_CTL4 0x2003
+#define CS42L84_ADC_CTL4_WNF_CF_SHIFT 4
+#define CS42L84_ADC_CTL4_WNF_EN_SHIFT 3
+#define CS42L84_ADC_CTL4_HPF_CF_SHIFT 1
+#define CS42L84_ADC_CTL4_HPF_EN_SHIFT 0
+
+#define CS42L84_DAC_CTL1 0x3000
+#define CS42L84_DAC_CTL1_UNMUTE BIT(0)
+//#define CS42L84_DAC_CTL1_DACB_INV_SHIFT 1
+//#define CS42L84_DAC_CTL1_DACA_INV_SHIFT 0
+#define CS42L84_DAC_CTL2 0x3001
+
+#define CS42L84_DAC_CHA_VOL_LSB 0x3004
+#define CS42L84_DAC_CHA_VOL_MSB 0x3005
+#define CS42L84_DAC_CHB_VOL_LSB 0x3006
+#define CS42L84_DAC_CHB_VOL_MSB 0x3007
+#define CS42L84_HP_VOL_CTL 0x3020
+#define CS42L84_HP_VOL_CTL_ZERO_CROSS BIT(1)
+#define CS42L84_HP_VOL_CTL_SOFT BIT(0)
+
+#define CS42L84_SRC_ASP_RX_CH1 0b1101
+#define CS42L84_SRC_ASP_RX_CH2 0b1110
+
+#define CS42L84_BUS_ASP_TX_SRC 0x4000
+#define CS42L84_BUS_ASP_TX_SRC_CH1_SHIFT 0
+#define CS42L84_BUS_DAC_SRC 0x4001
+#define CS42L84_BUS_DAC_SRC_DACA_SHIFT 0
+#define CS42L84_BUS_DAC_SRC_DACB_SHIFT 4
+
+#define CS42L84_ASP_CTL 0x5000
+#define CS42L84_ASP_CTL_BCLK_EN_SHIFT 1
+#define CS42L84_ASP_CTL_TDM_MODE BIT(2)
+#define CS42L84_ASP_FSYNC_CTL2 0x5010
+#define CS42L84_ASP_FSYNC_CTL2_BCLK_PERIOD_LO GENMASK(7, 1)
+#define CS42L84_ASP_FSYNC_CTL3 0x5011
+#define CS42L84_ASP_FSYNC_CTL3_BCLK_PERIOD_HI GENMASK(4, 0)
+#define CS42L84_ASP_DATA_CTL 0x5018
+
+#define CS42L84_ASP_RX_EN 0x5020
+#define CS42L84_ASP_RX_EN_CH1_SHIFT 0
+#define CS42L84_ASP_RX_EN_CH2_SHIFT 1
+#define CS42L84_ASP_TX_EN 0x5024
+#define CS42L84_ASP_TX_EN_CH1_SHIFT 0
+
+#define CS42L84_ASP_RX_CH1_CTL1 0x5028
+#define CS42L84_ASP_RX_CH1_CTL2 0x5029
+#define CS42L84_ASP_RX_CH1_WIDTH 0x502a
+#define CS42L84_ASP_RX_CH2_CTL1 0x502c
+#define CS42L84_ASP_RX_CH2_CTL2 0x502d
+#define CS42L84_ASP_RX_CH2_WIDTH 0x502e
+
+#define CS42L84_ASP_RX_CHx_CTL1_EDGE BIT(0)
+#define CS42L84_ASP_RX_CHx_CTL1_SLOT_START_LSB GENMASK(7, 1)
+#define CS42L84_ASP_RX_CHx_CTL2_SLOT_START_MSB GENMASK(2, 0)
+
+#define CS42L84_ASP_TX_CH1_CTL1 0x5068
+#define CS42L84_ASP_TX_CH1_CTL2 0x5069
+#define CS42L84_ASP_TX_CH1_WIDTH 0x506a
+#define CS42L84_ASP_TX_CH2_CTL1 0x506c
+#define CS42L84_ASP_TX_CH2_CTL2 0x506d
+#define CS42L84_ASP_TX_CH2_WIDTH 0x506e
+
+#define CS42L84_DEBOUNCE_TIME_125MS 0b001
+#define CS42L84_DEBOUNCE_TIME_500MS 0b011
+
+#define CS42L84_BOOT_TIME_US 3000
+#define CS42L84_CLOCK_SWITCH_DELAY_US 150
+#define CS42L84_PLL_LOCK_POLL_US 250
+#define CS42L84_PLL_LOCK_TIMEOUT_US 1250
+
+#endif /* __CS42L84_H__ */
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 657dce27eade..039b3ecb3b9b 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -12,16 +12,24 @@
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
#include "cs42xx8.h"
-static int cs42xx8_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int cs42xx8_i2c_probe(struct i2c_client *i2c)
{
- u32 ret = cs42xx8_probe(&i2c->dev,
- devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config));
+ int ret;
+ struct cs42xx8_driver_data *drvdata;
+
+ drvdata = (struct cs42xx8_driver_data *)i2c_get_match_data(i2c);
+ if (!drvdata)
+ return dev_err_probe(&i2c->dev, -EINVAL,
+ "failed to find driver data\n");
+
+ ret = cs42xx8_probe(&i2c->dev,
+ devm_regmap_init_i2c(i2c, &cs42xx8_regmap_config), drvdata);
if (ret)
return ret;
@@ -31,15 +39,19 @@ static int cs42xx8_i2c_probe(struct i2c_client *i2c,
return 0;
}
-static int cs42xx8_i2c_remove(struct i2c_client *i2c)
+static void cs42xx8_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
pm_runtime_disable(&i2c->dev);
-
- return 0;
}
-static struct i2c_device_id cs42xx8_i2c_id[] = {
+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},
{}
@@ -49,8 +61,8 @@ MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
static struct i2c_driver cs42xx8_i2c_driver = {
.driver = {
.name = "cs42xx8",
- .owner = THIS_MODULE,
- .pm = &cs42xx8_pm,
+ .pm = pm_ptr(&cs42xx8_pm),
+ .of_match_table = cs42xx8_of_match,
},
.probe = cs42xx8_i2c_probe,
.remove = cs42xx8_i2c_remove,
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 670ebfe12903..6a925f3f7137 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -13,7 +13,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/gpio/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm_params.h>
@@ -44,6 +44,9 @@ struct cs42xx8_priv {
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 */
@@ -173,28 +176,34 @@ static const struct snd_soc_dapm_route cs42xx8_adc3_dapm_routes[] = {
};
struct cs42xx8_ratios {
- unsigned int ratio;
- unsigned char speed;
- unsigned char mclk;
+ 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[] = {
- { 64, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_256(4) },
- { 96, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_384(4) },
- { 128, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_512(4) },
- { 192, CS42XX8_FM_QUAD, CS42XX8_FUNCMOD_MFREQ_768(4) },
- { 256, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_256(1) },
- { 384, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_384(1) },
- { 512, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_512(1) },
- { 768, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_768(1) },
- { 1024, CS42XX8_FM_SINGLE, CS42XX8_FUNCMOD_MFREQ_1024(1) }
+ { 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_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
cs42xx8->sysclk = freq;
@@ -204,8 +213,8 @@ static int cs42xx8_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct cs42xx8_priv *cs42xx8 = snd_soc_component_get_drvdata(component);
u32 val;
/* Set DAI format */
@@ -223,7 +232,7 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
break;
default:
- dev_err(codec->dev, "unsupported dai format\n");
+ dev_err(component->dev, "unsupported dai format\n");
return -EINVAL;
}
@@ -233,14 +242,14 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
/* Set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
cs42xx8->slave_mode = true;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
cs42xx8->slave_mode = false;
break;
default:
- dev_err(codec->dev, "unsupported master/slave mode\n");
+ dev_err(component->dev, "unsupported master/slave mode\n");
return -EINVAL;
}
@@ -251,41 +260,116 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ 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 = cs42xx8->sysclk / params_rate(params);
- u32 i, fm, val, mask;
+ 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++) {
- if (cs42xx8_ratios[i].ratio == ratio)
+ /* 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(codec->dev, "unsupported sysclk ratio\n");
+ dev_err(component->dev, "unsupported sysclk ratio\n");
return -EINVAL;
}
- mask = CS42XX8_FUNCMOD_MFREQ_MASK;
- val = cs42xx8_ratios[i].mclk;
+ cs42xx8->rate[tx] = params_rate(params);
- fm = cs42xx8->slave_mode ? CS42XX8_FM_AUTO : cs42xx8_ratios[i].speed;
+ 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) | val);
+ CS42XX8_FUNCMOD_xC_FM(tx, fm[tx]) | val);
return 0;
}
-static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
+static int cs42xx8_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+ 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;
- regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
- CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
+ /* 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;
}
@@ -294,7 +378,9 @@ 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,
- .digital_mute = cs42xx8_digital_mute,
+ .hw_free = cs42xx8_hw_free,
+ .mute_stream = cs42xx8_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver cs42xx8_dai = {
@@ -315,7 +401,6 @@ static struct snd_soc_dai_driver cs42xx8_dai = {
};
static const struct reg_default cs42xx8_reg[] = {
- { 0x01, 0x01 }, /* Chip I.D. and Revision Register */
{ 0x02, 0x00 }, /* Power Control */
{ 0x03, 0xF0 }, /* Functional Mode */
{ 0x04, 0x46 }, /* Interface Formats */
@@ -373,18 +458,18 @@ const struct regmap_config cs42xx8_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(cs42xx8_reg),
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
-static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
+static int cs42xx8_component_probe(struct snd_soc_component *component)
{
- struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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_codec_controls(codec, cs42xx8_adc3_snd_controls,
+ 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));
@@ -401,16 +486,16 @@ static int cs42xx8_codec_probe(struct snd_soc_codec *codec)
return 0;
}
-static const struct snd_soc_codec_driver cs42xx8_driver = {
- .probe = cs42xx8_codec_probe,
- .idle_bias_off = true,
-
- .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),
+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 = {
@@ -425,33 +510,33 @@ const struct cs42xx8_driver_data cs42888_data = {
};
EXPORT_SYMBOL_GPL(cs42888_data);
-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);
-EXPORT_SYMBOL_GPL(cs42xx8_of_match);
-
-int cs42xx8_probe(struct device *dev, struct regmap *regmap)
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata)
{
- const struct of_device_id *of_id = of_match_device(cs42xx8_of_match, dev);
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);
- if (of_id)
- cs42xx8->drvdata = of_id->data;
+ cs42xx8->regmap = regmap;
- if (!cs42xx8->drvdata) {
- dev_err(dev, "failed to find driver data\n");
- return -EINVAL;
- }
+ 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)) {
@@ -482,20 +567,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
/* Make sure hardware reset done */
msleep(5);
- cs42xx8->regmap = regmap;
- if (IS_ERR(cs42xx8->regmap)) {
- ret = PTR_ERR(cs42xx8->regmap);
- dev_err(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(cs42xx8->regmap, true);
-
/* Validate the chip ID */
ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
if (ret < 0) {
@@ -514,16 +585,14 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "found device, revision %X\n",
val & CS42XX8_CHIPID_REV_ID_MASK);
- regcache_cache_bypass(cs42xx8->regmap, false);
-
cs42xx8_dai.name = cs42xx8->drvdata->name;
/* Each adc supports stereo input */
cs42xx8_dai.capture.channels_max = cs42xx8->drvdata->num_adcs * 2;
- ret = snd_soc_register_codec(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &cs42xx8_driver, &cs42xx8_dai, 1);
if (ret) {
- dev_err(dev, "failed to register codec:%d\n", ret);
+ dev_err(dev, "failed to register component:%d\n", ret);
goto err_enable;
}
@@ -537,7 +606,6 @@ err_enable:
}
EXPORT_SYMBOL_GPL(cs42xx8_probe);
-#ifdef CONFIG_PM
static int cs42xx8_runtime_resume(struct device *dev)
{
struct cs42xx8_priv *cs42xx8 = dev_get_drvdata(dev);
@@ -549,6 +617,8 @@ static int cs42xx8_runtime_resume(struct device *dev)
return ret;
}
+ gpiod_set_value_cansleep(cs42xx8->gpiod_reset, 0);
+
ret = regulator_bulk_enable(ARRAY_SIZE(cs42xx8->supplies),
cs42xx8->supplies);
if (ret) {
@@ -560,6 +630,7 @@ static int cs42xx8_runtime_resume(struct device *dev)
msleep(5);
regcache_cache_only(cs42xx8->regmap, false);
+ regcache_mark_dirty(cs42xx8->regmap);
ret = regcache_sync(cs42xx8->regmap);
if (ret) {
@@ -587,16 +658,17 @@ static int cs42xx8_runtime_suspend(struct device *dev)
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_RUNTIME_PM_OPS(cs42xx8_runtime_suspend, cs42xx8_runtime_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(cs42xx8_pm) = {
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+ 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.");
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
index b2c10e537ef6..342389e8b1a8 100644
--- a/sound/soc/codecs/cs42xx8.h
+++ b/sound/soc/codecs/cs42xx8.h
@@ -22,7 +22,7 @@ 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);
+int cs42xx8_probe(struct device *dev, struct regmap *regmap, struct cs42xx8_driver_data *drvdata);
/* CS42888 register map */
#define CS42XX8_CHIPID 0x01 /* Chip ID */
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
new file mode 100644
index 000000000000..d9b3d73c8388
--- /dev/null
+++ b/sound/soc/codecs/cs43130.c
@@ -0,0 +1,2781 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs43130.c -- CS43130 ALSA Soc Audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Authors: Li Xu <li.xu@cirrus.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <sound/jack.h>
+
+#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(cs43130->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(cs43130->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(cs43130->dev,
+ "unsupported pll output ref clock: %u\n", freq_out);
+ return -EINVAL;
+ }
+
+ ret = cs43130_pll_config(component);
+ dev_dbg(cs43130->dev, "cs43130->pll_bypass = %d", cs43130->pll_bypass);
+ return ret;
+}
+
+static int cs43130_wait_for_completion(struct cs43130_private *cs43130, struct completion *to_poll,
+ int time)
+{
+ int stickies, offset, flag, ret;
+
+ if (cs43130->has_irq_line) {
+ ret = wait_for_completion_timeout(to_poll, msecs_to_jiffies(time));
+ if (ret == 0)
+ return -ETIMEDOUT;
+ else
+ return 0; // Discard number of jiffies left till timeout and return success
+ }
+
+ if (to_poll == &cs43130->xtal_rdy) {
+ offset = 0;
+ flag = CS43130_XTAL_RDY_INT;
+ } else if (to_poll == &cs43130->pll_rdy) {
+ offset = 0;
+ flag = CS43130_PLL_RDY_INT;
+ } else {
+ return -EINVAL;
+ }
+
+ return regmap_read_poll_timeout(cs43130->regmap, CS43130_INT_STATUS_1 + offset,
+ stickies, (stickies & flag),
+ 1000, time * 1000);
+}
+
+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(cs43130->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 = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+ }
+
+ 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 = cs43130_wait_for_completion(cs43130, &cs43130->xtal_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_XTAL_RDY_INT_MASK,
+ 1 << CS43130_XTAL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for XTAL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+ }
+
+ 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 = cs43130_wait_for_completion(cs43130, &cs43130->pll_rdy, 100);
+ regmap_update_bits(cs43130->regmap, CS43130_INT_MASK_1,
+ CS43130_PLL_RDY_INT_MASK,
+ 1 << CS43130_PLL_RDY_INT_SHIFT);
+ if (ret) {
+ dev_err(cs43130->dev, "Error waiting for PLL_READY interrupt: %d\n", ret);
+ return ret;
+ }
+
+ 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(cs43130->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_invert) {
+ case SND_SOC_DAIFMT_NB_NF:
+ sclk_edge = 1;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ sclk_edge = 0;
+ lrck_edge = 0;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sclk_edge = 1;
+ lrck_edge = 1;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ sclk_edge = 0;
+ lrck_edge = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (cs43130->dais[dai_id].dai_mode) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ dai_mode_val = 0;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dai_mode_val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ frm_size = bitwidth_sclk * params_channels(params);
+ 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(cs43130->dev, "Rate(%u) not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ if (cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
+ 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(cs43130->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(cs43130->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_CBP_CFP)
+ /* 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(cs43130->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(cs43130->dev, "Format not supported: SCLK freq is too low\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->dev,
+ "sclk = %u, fs = %d, bitwidth_dai = %u\n",
+ sclk, params_rate(params), bitwidth_dai);
+
+ dev_dbg(cs43130->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(cs43130->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(cs43130->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(cs43130->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(cs43130->dev, "Invalid HPIN event = 0x%x\n", event);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const char * const bypass_mux_text[] = {
+ "Internal",
+ "Alternative",
+};
+static SOC_ENUM_SINGLE_DECL(bypass_enum, SND_SOC_NOPM, 0, bypass_mux_text);
+static const struct snd_kcontrol_new bypass_ctrl = SOC_DAPM_ENUM("Switch", bypass_enum);
+
+static const struct snd_soc_dapm_widget hp_widgets[] = {
+ SND_SOC_DAPM_MUX("Bypass Switch", SND_SOC_NOPM, 0, 0, &bypass_ctrl),
+ 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)),
+
+/* Some devices have some extra analog widgets */
+#define NUM_ANALOG_WIDGETS 1
+
+ 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 const struct snd_soc_dapm_route 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"},
+ {"Bypass Switch", "Internal", "HiFi DAC"},
+ {"HPOUTA", NULL, "Bypass Switch"},
+ {"HPOUTB", NULL, "Bypass Switch"},
+
+/* Some devices have some extra analog routes */
+#define NUM_ANALOG_ROUTES 1
+ {"Bypass Switch", "Alternative", "Analog Playback"},
+};
+
+
+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_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ default:
+ dev_err(cs43130->dev, "unsupported mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_NF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_NF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_NB_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ cs43130->dais[codec_dai->id].dai_invert = SND_SOC_DAIFMT_IB_IF;
+ break;
+ default:
+ dev_err(cs43130->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ 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(cs43130->dev,
+ "unsupported audio format\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->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_CBC_CFC:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBC_CFC;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ cs43130->dais[codec_dai->id].dai_mode = SND_SOC_DAIFMT_CBP_CFP;
+ break;
+ default:
+ dev_err(cs43130->dev, "Unsupported DAI format.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(cs43130->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(cs43130->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(cs43130->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(cs43130->dev, "Invalid MCLK INT freq: %u\n", freq);
+ return -EINVAL;
+ }
+
+ if (source == CS43130_MCLK_SRC_EXT) {
+ cs43130->pll_bypass = true;
+ } else {
+ dev_err(cs43130->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 const 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 const 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 const 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 const struct reg_sequence hp_dis_cal_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0},
+};
+
+static const 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 const struct reg_sequence hp_dc_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x81},
+};
+
+static const 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 const struct reg_sequence hp_dc_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x91},
+};
+
+static const 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 const struct reg_sequence hp_ac_ch_l_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x80},
+ {CS43130_HP_LOAD_1, 0x82},
+};
+
+static const 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 const struct reg_sequence hp_ac_ch_r_seq2[] = {
+ {CS43130_HP_LOAD_1, 0x90},
+ {CS43130_HP_LOAD_1, 0x92},
+};
+
+static const 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 {
+ const struct reg_sequence *seq;
+ int size;
+ unsigned int msk;
+};
+
+static const 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 const 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;
+
+ switch (msk) {
+ case CS43130_HPLOAD_DC_INT:
+ case CS43130_HPLOAD_AC_INT:
+ break;
+ default:
+ return 0;
+ }
+
+ regmap_read(cs43130->regmap, CS43130_HP_LOAD_1, &reg);
+ 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, &reg);
+ impedance = reg >> 3;
+ regmap_read(cs43130->regmap, addr + 1, &reg);
+ 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(cs43130->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(cs43130->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,
+ const struct reg_sequence *seq, int seq_size,
+ unsigned int rslt_msk, int ac_idx)
+{
+ int ret;
+ unsigned int msk;
+ u16 ac_reg_val;
+
+ 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(cs43130->dev, "Timeout waiting for HPLOAD interrupt\n");
+ return -ETIMEDOUT;
+ }
+
+ dev_dbg(cs43130->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(cs43130->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;
+ const 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, &reg);
+
+ 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(cs43130->dev, "Set HP output control. DC threshold\n");
+ for (i = 0; i < CS43130_DC_THRESHOLD; i++)
+ dev_dbg(cs43130->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;
+ 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;
+ }
+
+ 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(cs43130->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(cs43130->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(cs43130->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(cs43130->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(cs43130->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(cs43130->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(cs43130->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(cs43130->dev, "Crystal err: clock is not running\n");
+ return IRQ_HANDLED;
+ }
+
+ if (stickies[0] & CS43130_HP_UNPLUG_INT) {
+ dev_dbg(cs43130->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(cs43130->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(cs43130->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ cs43130->hpload_done = false;
+ if (cs43130->dc_meas) {
+ ret = sysfs_create_groups(&cs43130->dev->kobj, hpload_groups);
+ if (ret)
+ return ret;
+
+ cs43130->wq = create_singlethread_workqueue("cs43130_hp");
+ if (!cs43130->wq) {
+ sysfs_remove_groups(&cs43130->dev->kobj, hpload_groups);
+ return -ENOMEM;
+ }
+ INIT_WORK(&cs43130->work, cs43130_imp_meas);
+ }
+
+ regmap_read(cs43130->regmap, CS43130_INT_STATUS_1, &reg);
+ regmap_read(cs43130->regmap, CS43130_HP_STATUS, &reg);
+ 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 const struct snd_soc_component_driver soc_component_dev_cs43130_digital = {
+ .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,
+ /* Don't take into account the ending analog widgets and routes */
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets) - NUM_ANALOG_WIDGETS,
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes) - NUM_ANALOG_ROUTES,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_cs43130_analog = {
+ .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,
+ .dapm_widgets = hp_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hp_widgets),
+ .dapm_routes = hp_routes,
+ .num_dapm_routes = ARRAY_SIZE(hp_routes),
+};
+
+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 cs43130_private *cs43130)
+{
+ unsigned int val;
+ int i;
+
+ if (device_property_read_u32(cs43130->dev, "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(cs43130->dev,
+ "Invalid cirrus,xtal-ibias value: %d\n", val);
+ return -EINVAL;
+ }
+
+ cs43130->dc_meas = device_property_read_bool(cs43130->dev, "cirrus,dc-measure");
+ cs43130->ac_meas = device_property_read_bool(cs43130->dev, "cirrus,ac-measure");
+
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,ac-freq", cs43130->ac_freq,
+ CS43130_AC_FREQ)) {
+ for (i = 0; i < CS43130_AC_FREQ; i++)
+ cs43130->ac_freq[i] = cs43130_ac_freq[i];
+ }
+
+ if (!device_property_read_u16_array(cs43130->dev, "cirrus,dc-threshold",
+ cs43130->dc_threshold,
+ CS43130_DC_THRESHOLD)) {
+ 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)
+{
+ const struct snd_soc_component_driver *component_driver;
+ 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;
+
+ cs43130->dev = &client->dev;
+
+ 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 (dev_fwnode(cs43130->dev)) {
+ ret = cs43130_handle_device_data(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(cs43130->dev,
+ ARRAY_SIZE(cs43130->supplies),
+ cs43130->supplies);
+ if (ret != 0) {
+ dev_err(cs43130->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(cs43130->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ cs43130->reset_gpio = devm_gpiod_get_optional(cs43130->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(cs43130->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(cs43130->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, &reg);
+ if (ret < 0) {
+ dev_err(cs43130->dev, "Get Revision ID failed\n");
+ goto err;
+ }
+
+ dev_info(cs43130->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);
+
+ if (!client->irq) {
+ dev_dbg(cs43130->dev, "IRQ not found, will poll instead\n");
+ cs43130->has_irq_line = 0;
+ } else {
+ ret = devm_request_threaded_irq(cs43130->dev, client->irq,
+ NULL, cs43130_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs43130", cs43130);
+ if (ret != 0) {
+ dev_err(cs43130->dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ cs43130->has_irq_line = 1;
+ }
+
+ cs43130->mclk_int_src = CS43130_MCLK_SRC_RCO;
+
+ pm_runtime_set_autosuspend_delay(cs43130->dev, 100);
+ pm_runtime_use_autosuspend(cs43130->dev);
+ pm_runtime_set_active(cs43130->dev);
+ pm_runtime_enable(cs43130->dev);
+
+ switch (cs43130->dev_id) {
+ case CS43130_CHIP_ID:
+ case CS43131_CHIP_ID:
+ component_driver = &soc_component_dev_cs43130_analog;
+ break;
+ case CS43198_CHIP_ID:
+ case CS4399_CHIP_ID:
+ component_driver = &soc_component_dev_cs43130_digital;
+ break;
+ }
+
+ ret = devm_snd_soc_register_component(cs43130->dev, component_driver,
+ cs43130_dai, ARRAY_SIZE(cs43130_dai));
+ if (ret < 0) {
+ dev_err(cs43130->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(cs43130->dev, &dev_attr_hpload_dc_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_dc_r);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_l);
+ device_remove_file(cs43130->dev, &dev_attr_hpload_ac_r);
+ }
+
+ gpiod_set_value_cansleep(cs43130->reset_gpio, 0);
+
+ pm_runtime_disable(cs43130->dev);
+ regulator_bulk_disable(CS43130_NUM_SUPPLIES, cs43130->supplies);
+}
+
+static int 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 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 = {
+ RUNTIME_PM_OPS(cs43130_runtime_suspend, cs43130_runtime_resume, NULL)
+};
+
+#if IS_ENABLED(CONFIG_OF)
+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);
+#endif
+
+#if IS_ENABLED(CONFIG_ACPI)
+static const struct acpi_device_id cs43130_acpi_match[] = {
+ { "CSC4399", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match);
+#endif
+
+
+static const struct i2c_device_id cs43130_i2c_id[] = {
+ {"cs43130"},
+ {"cs4399"},
+ {"cs43131"},
+ {"cs43198"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
+
+static struct i2c_driver cs43130_i2c_driver = {
+ .driver = {
+ .name = "cs43130",
+ .of_match_table = of_match_ptr(cs43130_of_match),
+ .acpi_match_table = ACPI_PTR(cs43130_acpi_match),
+ .pm = pm_ptr(&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 <li.xu@cirrus.com>");
+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 000000000000..dbdb5b262f1b
--- /dev/null
+++ b/sound/soc/codecs/cs43130.h
@@ -0,0 +1,541 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ALSA SoC CS43130 codec driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Li Xu <li.xu@cirrus.com>
+ */
+
+#ifndef __CS43130_H__
+#define __CS43130_H__
+
+#include <linux/math.h>
+
+/* 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;
+ unsigned int dai_invert;
+};
+
+struct cs43130_private {
+ struct device *dev;
+ 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;
+ bool has_irq_line;
+
+ /* 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 000000000000..b726e22ef57d
--- /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 <shc_work@mail.ru>
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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_CBC_CFC:
+ 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" },
+ { }
+};
+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 <shc_work@mail.ru>");
+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 000000000000..d9a9c34fffe3
--- /dev/null
+++ b/sound/soc/codecs/cs4349.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs4349.c -- CS4349 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#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);
+}
+
+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;
+}
+
+static const struct dev_pm_ops cs4349_runtime_pm = {
+ 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"},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id);
+
+static struct i2c_driver cs4349_i2c_driver = {
+ .driver = {
+ .name = "cs4349",
+ .of_match_table = cs4349_of_match,
+ .pm = pm_ptr(&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 <tim.howe@cirrus.com>");
+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 000000000000..bf31405f7f05
--- /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 <Tim.Howe@cirrus.com>
+ */
+
+#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 000000000000..29a2bcfb3048
--- /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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#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(snd_soc_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",
+ snd_soc_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 = 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 <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Jaswinder Jassal <jjassal@opensource.cirrus.com>");
+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 000000000000..e2a839fae4fc
--- /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 <rf@opensource.wolfsonmicro.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l24-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = cs47l24_remove,
+};
+
+module_platform_driver(cs47l24_codec_driver);
+
+MODULE_DESCRIPTION("ASoC CS47L24 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+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 000000000000..9fd4b41f1f3a
--- /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 <rf@opensource.wolfsonmicro.com>
+ */
+
+#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 000000000000..85555c7a2e4b
--- /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 <linux/delay.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l35-dsp-voicectrl") == 0) {
+ n_adsp = 2;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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 <piotrs@opensource.cirrus.com>");
+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 000000000000..d34f4e8c26d3
--- /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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l85-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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 <nariman@opensource.cirrus.com>");
+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 000000000000..a9e703981f37
--- /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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#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(snd_soc_rtd_to_codec(rtd, 0)->name, "cs47l90-dsp-voicectrl") == 0) {
+ n_adsp = 5;
+ } else if (strcmp(snd_soc_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",
+ snd_soc_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 = 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 <nikesh@opensource.cirrus.com>");
+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 000000000000..2c355c61acd8
--- /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 <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+
+#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(snd_soc_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",
+ snd_soc_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 = 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 <stuarth@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cs47l92-codec");
diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
new file mode 100644
index 000000000000..8ff3652a010e
--- /dev/null
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -0,0 +1,538 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Regmap tables and other data for Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/device.h>
+#include <linux/linear_range.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+
+#include "cs48l32.h"
+
+static const struct reg_sequence cs48l32_reva_patch[] = {
+ { 0x00001044, 0x0005000f },
+ { 0x00001c34, 0x000037e8 },
+ { 0x000046d8, 0x00000fe0 },
+};
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch,
+ ARRAY_SIZE(cs48l32_reva_patch));
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to apply patch\n");
+
+ return 0;
+}
+
+static const struct reg_default cs48l32_reg_default[] = {
+ { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */
+ { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */
+ { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */
+ { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */
+ { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */
+ { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */
+ { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */
+ { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */
+ { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */
+ { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */
+ { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */
+ { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */
+ { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */
+ { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */
+ { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */
+ { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */
+ { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */
+ { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */
+ { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */
+ { 0x00001400, 0x00000002 }, /* CLOCK32K */
+ { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */
+ { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */
+ { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */
+ { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */
+ { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */
+ { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */
+ { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */
+ { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */
+ { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */
+ { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */
+ { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */
+ { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */
+ { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */
+ { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */
+ { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */
+ { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */
+ { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */
+ { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */
+ { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */
+ { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */
+ { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */
+ { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */
+ { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */
+ { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */
+ { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */
+ { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */
+ { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */
+ { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */
+ { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */
+ { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */
+ { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */
+ { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */
+ { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */
+ { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */
+ { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */
+ { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */
+ { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */
+ { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */
+ { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */
+ { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */
+ { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */
+ { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */
+ { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */
+ { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */
+ { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */
+ { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */
+ { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */
+ { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */
+ { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */
+ { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */
+ { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */
+ { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */
+ { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */
+ { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */
+ { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */
+ { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */
+ { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */
+ { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */
+ { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */
+ { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */
+ { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */
+ { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */
+ { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */
+ { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */
+ { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */
+ { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */
+ { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */
+ { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */
+ { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */
+ { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */
+ { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */
+ { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */
+ { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */
+ { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */
+ { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */
+ { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */
+ { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */
+ { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */
+ { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */
+ { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */
+ { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */
+ { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */
+ { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */
+ { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */
+ { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */
+ { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */
+ { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */
+ { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */
+ { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */
+ { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */
+ { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */
+ { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */
+ { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */
+ { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */
+ { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */
+ { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */
+ { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */
+ { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */
+ { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */
+ { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */
+ { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */
+ { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */
+ { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */
+ { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */
+ { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */
+ { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */
+ { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */
+ { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */
+ { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */
+ { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */
+ { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */
+ { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */
+ { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */
+ { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */
+ { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */
+ { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */
+ { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */
+ { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */
+ { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */
+ { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */
+ { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */
+ { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */
+ { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */
+ { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */
+ { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */
+ { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */
+ { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */
+ { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */
+ { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */
+ { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */
+ { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */
+ { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */
+ { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */
+ { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */
+ { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */
+ { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */
+ { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */
+ { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */
+ { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */
+ { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */
+ { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */
+ { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */
+ { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */
+ { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */
+ { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */
+ { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */
+ { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */
+ { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */
+ { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */
+ { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */
+ { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */
+ { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */
+ { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */
+ { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */
+ { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */
+ { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */
+ { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */
+ { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */
+ { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */
+ { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */
+ { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */
+ { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */
+ { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */
+ { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */
+ { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */
+ { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */
+ { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */
+ { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */
+ { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */
+ { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */
+ { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */
+ { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */
+ { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */
+ { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */
+ { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */
+ { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */
+ { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */
+ { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */
+ { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */
+ { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */
+ { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */
+ { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */
+ { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */
+ { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */
+ { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */
+ { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */
+ { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */
+ { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */
+ { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */
+ { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */
+ { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */
+ { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */
+ { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */
+ { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */
+ { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */
+ { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */
+ { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */
+ { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */
+ { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */
+ { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */
+ { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */
+ { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */
+ { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */
+ { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */
+ { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */
+ { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */
+ { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */
+ { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */
+ { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */
+ { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */
+ { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */
+ { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */
+ { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */
+ { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */
+ { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */
+ { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */
+ { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */
+ { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */
+ { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */
+ { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */
+ { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */
+ { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */
+ { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */
+ { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */
+ { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */
+ { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */
+ { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */
+ { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */
+ { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */
+ { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */
+ { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */
+ { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */
+ { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */
+ { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */
+ { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */
+ { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */
+ { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */
+ { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */
+ { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */
+ { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */
+ { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */
+ { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */
+ { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */
+ { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */
+ { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */
+ { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */
+ { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */
+ { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */
+ { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */
+ { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */
+ { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */
+ { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */
+ { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */
+ { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */
+ { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */
+ { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */
+ { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */
+ { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */
+ { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */
+ { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */
+ { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */
+ { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */
+ { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */
+ { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */
+ { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */
+ { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */
+ { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */
+ { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */
+ { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */
+ { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */
+ { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */
+ { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */
+ { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */
+ { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */
+ { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */
+ { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */
+ { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */
+ { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */
+ { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */
+ { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */
+ { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */
+ { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */
+ { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */
+ { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */
+ { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */
+ { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */
+ { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */
+ { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */
+ { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */
+ { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */
+ { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */
+ { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */
+ { 0x0000b800, 0x00000000 }, /* US_CONTROL */
+ { 0x0000b804, 0x00002020 }, /* US1_CONTROL */
+ { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */
+ { 0x0000b814, 0x00002020 }, /* US2_CONTROL */
+ { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */
+ { 0x00018110, 0x00000700 }, /* IRQ1_MASK_1 */
+ { 0x00018114, 0x00000004 }, /* IRQ1_MASK_2 */
+ { 0x00018120, 0x03ff0000 }, /* IRQ1_MASK_5 */
+ { 0x00018124, 0x00000103 }, /* IRQ1_MASK_6 */
+ { 0x00018128, 0x003f0000 }, /* IRQ1_MASK_7 */
+ { 0x00018130, 0xff00000f }, /* IRQ1_MASK_9 */
+ { 0x00018138, 0xffff0000 }, /* IRQ1_MASK_11 */
+};
+
+static bool cs48l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1:
+ case CS48L32_OUTPUT_SYS_CLK:
+ case CS48L32_AUXPDM_CTRL:
+ case CS48L32_AUXPDM_CTRL2:
+ case CS48L32_CLOCK32K:
+ case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4:
+ case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK:
+ case CS48L32_CHARGE_PUMP1:
+ case CS48L32_LDO2_CTRL1:
+ case CS48L32_MICBIAS_CTRL1:
+ case CS48L32_MICBIAS_CTRL5:
+ case CS48L32_IRQ1_CTRL_AOD:
+ case CS48L32_INPUT_CONTROL:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_RATE_CONTROL:
+ case CS48L32_INPUT_CONTROL2:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_INPUT1_CONTROL1:
+ case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2:
+ case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2:
+ case CS48L32_INPUT2_CONTROL1:
+ case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2:
+ case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2:
+ case CS48L32_INPUT_HPF_CONTROL:
+ case CS48L32_INPUT_VOL_CONTROL:
+ case CS48L32_AUXPDM_CONTROL1:
+ case CS48L32_AUXPDM_CONTROL2:
+ case CS48L32_AUXPDM1_CONTROL1:
+ case CS48L32_AUXPDM2_CONTROL1:
+ case CS48L32_ADC1L_ANA_CONTROL1:
+ case CS48L32_ADC1R_ANA_CONTROL1:
+ case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5:
+ case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5:
+ case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4:
+ case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4:
+ case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1:
+ case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1:
+ case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1:
+ case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4:
+ case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4:
+ case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4:
+ case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4:
+ case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4:
+ case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4:
+ case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4:
+ case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4:
+ case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2:
+ case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2:
+ case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2:
+ case CS48L32_FX_SAMPLE_RATE:
+ case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2:
+ case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG:
+ case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG:
+ case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG:
+ case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG:
+ case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2:
+ case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF:
+ case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4:
+ case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4:
+ case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2:
+ case CS48L32_COMFORT_NOISE_GENERATOR:
+ case CS48L32_US_CONTROL:
+ case CS48L32_US1_CONTROL:
+ case CS48L32_US1_DET_CONTROL:
+ case CS48L32_US2_CONTROL:
+ case CS48L32_US2_DET_CONTROL:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs48l32_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_FLL1_CONTROL5:
+ case CS48L32_FLL1_CONTROL6:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * The bus bridge requires DSP packed memory registers to be accessed in
+ * aligned block multiples.
+ * Mark precious to prevent regmap debugfs causing an illegal bus transaction.
+ */
+static bool cs48l32_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs48l32_regmap = {
+ .name = "cs48l32",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .pad_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS48L32_DSP1_PMEM_LAST,
+ .readable_reg = &cs48l32_readable_register,
+ .volatile_reg = &cs48l32_volatile_register,
+ .precious_reg = &cs48l32_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs48l32_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default),
+};
+
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
+{
+ cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
+
+ return PTR_ERR_OR_ZERO(cs48l32->regmap);
+}
diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c
new file mode 100644
index 000000000000..a306af4289ad
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.c
@@ -0,0 +1,4076 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <dt-bindings/sound/cs48l32.h>
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string_choices.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs48l32.h"
+
+static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" };
+
+static const struct cs_dsp_region cs48l32_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
+ { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2800000 },
+ { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3400000 },
+};
+
+static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = {
+ { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 },
+ { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 },
+ { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 },
+};
+
+static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = {
+ CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0,
+};
+
+static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = {
+ .ext = cs48l32_dsp1_sram_ext_regs,
+ .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs),
+ .pwd = cs48l32_dsp1_sram_pwd_regs,
+ .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs),
+};
+
+static const char * const cs48l32_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "ASP1RX1",
+ "ASP1RX2",
+ "ASP1RX3",
+ "ASP1RX4",
+ "ASP1RX5",
+ "ASP1RX6",
+ "ASP1RX7",
+ "ASP1RX8",
+ "ASP2RX1",
+ "ASP2RX2",
+ "ASP2RX3",
+ "ASP2RX4",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "Ultrasonic 1",
+ "Ultrasonic 2",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP1.7",
+ "DSP1.8",
+};
+
+static unsigned int cs48l32_mixer_values[] = {
+ 0x000, /* Silence (mute) */
+ 0x004, /* Tone generator 1 */
+ 0x005, /* Tone generator 2 */
+ 0x00C, /* Noise Generator */
+ 0x010, /* IN1L signal path */
+ 0x011, /* IN1R signal path */
+ 0x012, /* IN2L signal path */
+ 0x013, /* IN2R signal path */
+ 0x020, /* ASP1 RX1 */
+ 0x021, /* ASP1 RX2 */
+ 0x022, /* ASP1 RX3 */
+ 0x023, /* ASP1 RX4 */
+ 0x024, /* ASP1 RX5 */
+ 0x025, /* ASP1 RX6 */
+ 0x026, /* ASP1 RX7 */
+ 0x027, /* ASP1 RX8 */
+ 0x030, /* ASP2 RX1 */
+ 0x031, /* ASP2 RX2 */
+ 0x032, /* ASP2 RX3 */
+ 0x033, /* ASP2 RX4 */
+ 0x098, /* ISRC1 INT1 */
+ 0x099, /* ISRC1 INT2 */
+ 0x09a, /* ISRC1 INT3 */
+ 0x09b, /* ISRC1 INT4 */
+ 0x09C, /* ISRC1 DEC1 */
+ 0x09D, /* ISRC1 DEC2 */
+ 0x09e, /* ISRC1 DEC3 */
+ 0x09f, /* ISRC1 DEC4 */
+ 0x0A0, /* ISRC2 INT1 */
+ 0x0A1, /* ISRC2 INT2 */
+ 0x0A4, /* ISRC2 DEC1 */
+ 0x0A5, /* ISRC2 DEC2 */
+ 0x0A8, /* ISRC3 INT1 */
+ 0x0A9, /* ISRC3 INT2 */
+ 0x0AC, /* ISRC3 DEC1 */
+ 0x0AD, /* ISRC3 DEC2 */
+ 0x0B8, /* EQ1 */
+ 0x0B9, /* EQ2 */
+ 0x0BA, /* EQ3 */
+ 0x0BB, /* EQ4 */
+ 0x0C0, /* DRC1 Left */
+ 0x0C1, /* DRC1 Right */
+ 0x0C2, /* DRC2 Left */
+ 0x0C3, /* DRC2 Right */
+ 0x0C8, /* LHPF1 */
+ 0x0C9, /* LHPF2 */
+ 0x0CA, /* LHPF3 */
+ 0x0CB, /* LHPF4 */
+ 0x0D8, /* Ultrasonic 1 */
+ 0x0D9, /* Ultrasonic 2 */
+ 0x100, /* DSP1 channel 1 */
+ 0x101, /* DSP1 channel 2 */
+ 0x102, /* DSP1 channel 3 */
+ 0x103, /* DSP1 channel 4 */
+ 0x104, /* DSP1 channel 5 */
+ 0x105, /* DSP1 channel 6 */
+ 0x106, /* DSP1 channel 7 */
+ 0x107, /* DSP1 channel 8 */
+};
+static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values));
+#define CS48L32_NUM_MIXER_INPUTS ARRAY_SIZE(cs48l32_mixer_values)
+
+static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0);
+
+static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(cs48l32->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out some clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val);
+ if (ret)
+ dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n",
+ __func__, ret, i);
+ }
+
+ udelay(300);
+}
+
+static const char * const cs48l32_rate_text[] = {
+ "Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4",
+};
+
+static const unsigned int cs48l32_rate_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text));
+
+static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Prevent any mixer mux changes while we do this */
+ mutex_lock(&cs48l32_codec->rate_lock);
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ mutex_unlock(&cs48l32_codec->rate_lock);
+
+ return ret;
+}
+
+static const char * const cs48l32_sample_rate_text[] = {
+ "12kHz",
+ "24kHz",
+ "48kHz",
+ "96kHz",
+ "192kHz",
+ "384kHz",
+ "768kHz",
+ "11.025kHz",
+ "22.05kHz",
+ "44.1kHz",
+ "88.2kHz",
+ "176.4kHz",
+ "352.8kHz",
+ "705.6kHz",
+ "8kHz",
+ "16kHz",
+ "32kHz",
+};
+
+static const unsigned int cs48l32_sample_rate_val[] = {
+ 0x01, /* 12kHz */
+ 0x02, /* 24kHz */
+ 0x03, /* 48kHz */
+ 0x04, /* 96kHz */
+ 0x05, /* 192kHz */
+ 0x06, /* 384kHz */
+ 0x07, /* 768kHz */
+ 0x09, /* 11.025kHz */
+ 0x0a, /* 22.05kHz */
+ 0x0b, /* 44.1kHz */
+ 0x0c, /* 88.2kHz */
+ 0x0d, /* 176.4kHz */
+ 0x0e, /* 352.8kHz */
+ 0x0f, /* 705.6kHz */
+ 0x11, /* 8kHz */
+ 0x12, /* 16kHz */
+ 0x13, /* 32kHz */
+};
+static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text));
+#define CS48L32_SAMPLE_RATE_ENUM_SIZE ARRAY_SIZE(cs48l32_sample_rate_val)
+
+static const struct soc_enum cs48l32_sample_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+};
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mux, src_val, in_type;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case CS48L32_IN1L_CONTROL1:
+ in_type = cs48l32_codec->in_type[0][mux];
+ break;
+ case CS48L32_IN1R_CONTROL1:
+ in_type = cs48l32_codec->in_type[1][mux];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_val = mux << e->shift_l;
+
+ if (in_type == CS48L32_IN_TYPE_SE)
+ src_val |= 1 << CS48L32_INx_SRC_SHIFT;
+
+ ret = snd_soc_component_update_bits(dapm->component,
+ e->reg,
+ CS48L32_INx_SRC_MASK,
+ src_val);
+ if (ret > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return ret;
+}
+
+static const char * const cs48l32_inmux_texts[] = {
+ "Analog 1", "Analog 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static const struct snd_kcontrol_new cs48l32_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+};
+
+static const char * const cs48l32_dmode_texts[] = {
+ "Analog", "Digital",
+};
+
+static int cs48l32_dmode_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 soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mode;
+ int ret, result;
+
+ mode = ucontrol->value.enumerated.item[0];
+ switch (mode) {
+ case 0:
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ result = snd_soc_component_update_bits(component,
+ e->reg,
+ BIT(CS48L32_IN1_MODE_SHIFT),
+ 0);
+ if (result < 0) {
+ dev_err(component->dev, "Failed to set input mode: %d\n", result);
+ return result;
+ }
+
+ usleep_range(200, 300);
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ if (result > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL);
+
+ return result;
+ case 1:
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ default:
+ return -EINVAL;
+ }
+}
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum,
+ CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_MODE_SHIFT,
+ cs48l32_dmode_texts);
+
+static const struct snd_kcontrol_new cs48l32_dmode_mux[] = {
+ SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_dmode_put),
+};
+
+static const char * const cs48l32_in_texts[] = {
+ "IN1L", "IN1R", "IN2L", "IN2R",
+};
+static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT);
+
+static const char * const cs48l32_us_freq_texts[] = {
+ "16-24kHz", "20-28kHz",
+};
+
+static const unsigned int cs48l32_us_freq_val[] = {
+ 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_freq[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+};
+
+static const unsigned int cs48l32_us_in_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_inmux[] = {
+ SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]),
+ SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]),
+};
+
+static const char * const cs48l32_us_det_thr_texts[] = {
+ "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB",
+};
+
+static const struct soc_enum cs48l32_us_det_thr[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+};
+
+static const char * const cs48l32_us_det_num_texts[] = {
+ "1 Sample",
+ "2 Samples",
+ "4 Samples",
+ "8 Samples",
+ "16 Samples",
+ "32 Samples",
+ "64 Samples",
+ "128 Samples",
+ "256 Samples",
+ "512 Samples",
+ "1024 Samples",
+ "2048 Samples",
+ "4096 Samples",
+ "8192 Samples",
+ "16384 Samples",
+ "32768 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_num[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+};
+
+static const char * const cs48l32_us_det_hold_texts[] = {
+ "0 Samples",
+ "31 Samples",
+ "63 Samples",
+ "127 Samples",
+ "255 Samples",
+ "511 Samples",
+ "1023 Samples",
+ "2047 Samples",
+ "4095 Samples",
+ "8191 Samples",
+ "16383 Samples",
+ "32767 Samples",
+ "65535 Samples",
+ "131071 Samples",
+ "262143 Samples",
+ "524287 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_hold[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+};
+
+static const struct soc_enum cs48l32_us_output_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const char * const cs48l32_us_det_lpf_cut_texts[] = {
+ "1722Hz", "833Hz", "408Hz", "203Hz",
+};
+
+static const struct soc_enum cs48l32_us_det_lpf_cut[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+};
+
+static const char * const cs48l32_us_det_dcy_texts[] = {
+ "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms",
+};
+
+static const struct soc_enum cs48l32_us_det_dcy[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const char * const cs48l32_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VD_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VI_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static const char * const cs48l32_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum,
+ CS48L32_INPUT_HPF_CONTROL,
+ CS48L32_IN_HPF_CUT_SHIFT,
+ cs48l32_in_hpf_cut_text);
+
+static const char * const cs48l32_in_dmic_osr_text[] = {
+ "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz",
+};
+
+static const struct soc_enum cs48l32_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+};
+
+static bool cs48l32_is_input_enabled(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int input_active;
+
+ input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL);
+ switch (reg) {
+ case CS48L32_IN1L_CONTROL1:
+ return input_active & BIT(CS48L32_IN1L_EN_SHIFT);
+ case CS48L32_IN1R_CONTROL1:
+ return input_active & BIT(CS48L32_IN1R_EN_SHIFT);
+ case CS48L32_IN2L_CONTROL1:
+ return input_active & BIT(CS48L32_IN2L_EN_SHIFT);
+ case CS48L32_IN2R_CONTROL1:
+ return input_active & BIT(CS48L32_IN2R_EN_SHIFT);
+ default:
+ return false;
+ }
+}
+
+static int cs48l32_in_rate_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;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, e->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const struct soc_enum cs48l32_input_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static int cs48l32_low_power_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);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, mc->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static const struct soc_enum noise_gen_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_RATE_SHIFT,
+ CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_auxpdm_freq_texts[] = {
+ "3.072MHz", "2.048MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq,
+ CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq,
+ CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static const char * const cs48l32_auxpdm_src_texts[] = {
+ "Analog", "IN1 Digital", "IN2 Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT1_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT2_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in),
+ SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in),
+};
+
+static const unsigned int cs48l32_auxpdm_analog_in_val[] = {
+ 0x0, 0x1,
+};
+
+static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]),
+ SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct soc_enum cs48l32_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_fx_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE,
+ CS48L32_FX_RATE_SHIFT,
+ CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+static const struct soc_enum cs48l32_lhpf_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+};
+
+static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ __be32 *data = (__be32 *)ucontrol->value.bytes.data;
+ s16 val = (s16)be32_to_cpu(*data);
+
+ if (abs(val) > CS48L32_LHPF_MAX_COEFF) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+
+static const char * const cs48l32_eq_mode_text[] = {
+ "Low-pass", "High-pass",
+};
+
+static const struct soc_enum cs48l32_eq_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+};
+
+static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int item;
+
+ item = snd_soc_enum_val_to_item(e, cs48l32_codec->eq_mode[e->shift_l]);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_eq_mode_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 cs48l32_codec *cs48l32_codec = 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;
+ bool changed = false;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->eq_mode[e->shift_l] != val) {
+ cs48l32_codec->eq_mode[e->shift_l] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = ctl->max;
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int cached_rate;
+ const unsigned int rate_num = e->mask;
+ int item;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ cached_rate = cs48l32_codec->dsp_dma_rates[rate_num];
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_dsp_rate_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ const unsigned int rate_num = e->mask;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ bool changed = false;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->dsp_dma_rates[rate_num] != val) {
+ cs48l32_codec->dsp_dma_rates[rate_num] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static const struct soc_enum cs48l32_dsp_rate_enum[] = {
+ /* RX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 0,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 1,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 2,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 3,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 4,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 5,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 6,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 7,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ /* TX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 8,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 9,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 10,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 11,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 12,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 13,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 14,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 15,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+};
+
+static int cs48l32_dsp_pre_run(struct wm_adsp *dsp)
+{
+ struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp);
+ unsigned int reg;
+ const u8 *rate = cs48l32_codec->dsp_dma_rates;
+ int i;
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1;
+ for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1;
+ for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ usleep_range(300, 600);
+
+ return 0;
+}
+
+static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return;
+
+err:
+ dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+}
+
+static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ /* disable power-off */
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0x3);
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* power-up the banks in sequence */
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0x1);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+
+ ret = regmap_write(regmap, regs->pwd[i], 0x3);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+ }
+
+ return 0;
+
+err:
+ dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+ cs48l32_dsp_memory_disable(cs48l32_codec, regs);
+
+ return ret;
+}
+
+static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
+ unsigned int freqsel_reg)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ struct wm_adsp *dsp = &cs48l32_codec->dsp;
+ int ret;
+ unsigned int freq, freq_sel, freq_sts;
+
+ if (!freq_reg)
+ return -EINVAL;
+
+ ret = regmap_read(regmap, freq_reg, &freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret);
+ return ret;
+ }
+
+ if (freqsel_reg) {
+ freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT;
+
+ ret = regmap_read(regmap, freqsel_reg, &freq_sel);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret);
+ return ret;
+ }
+ freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT;
+
+ if (freq_sts != freq_sel) {
+ dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n",
+ freq_sel, freq_sts);
+ return -ETIMEDOUT;
+ }
+ }
+
+ freq &= CS48L32_DSP_CLK_FREQ_MASK;
+ freq >>= CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1);
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t cs48l32_irq(int irq, void *data)
+{
+ static const unsigned int eint1_regs[] = {
+ CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9,
+ CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7
+ };
+ u32 reg_vals[4];
+ struct cs48l32_codec *cs48l32_codec = data;
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ irqreturn_t result = IRQ_NONE;
+ unsigned int eint_pending;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals));
+
+ ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+ if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0)
+ goto out;
+
+ ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals));
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) {
+ reg_vals[i] &= ~reg_vals[i + 1];
+ regmap_write(regmap, eint1_regs[i], reg_vals[i]);
+ }
+
+ if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK)
+ wm_adsp_compr_handle_irq(&cs48l32_codec->dsp);
+
+ if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n");
+ wm_halo_bus_error(irq, &cs48l32_codec->dsp);
+ }
+
+ if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n");
+ wm_halo_wdt_expire(irq, &cs48l32_codec->dsp);
+ }
+
+ result = IRQ_HANDLED;
+
+out:
+ pm_runtime_put_autosuspend(cs48l32_codec->core.dev);
+
+ return result;
+}
+
+static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq,
+ int src, unsigned int *val)
+{
+ freq /= 15625; /* convert to 1/64ths of 1MHz */
+ *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ return 0;
+}
+
+static int cs48l32_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return CS48L32_SYSCLK_RATE_6MHZ;
+ case 11289600:
+ case 12288000:
+ return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val;
+
+ switch (source) {
+ case CS48L32_PDMCLK_SRC_IN1_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN2_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN3_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN4_PDMCLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM1_CLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM2_CLK:
+ val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT;
+ break;
+ default:
+ dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2,
+ CS48L32_PDM_FLLCLK_SRC_MASK, val);
+}
+
+static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ char *name;
+ unsigned int reg;
+ unsigned int mask = CS48L32_SYSCLK_SRC_MASK;
+ unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = CS48L32_SYSTEM_CLOCK1;
+ clk = &cs48l32_codec->sysclk;
+ clk_freq_sel = cs48l32_get_sysclk_setting(freq);
+ mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK;
+ break;
+ case CS48L32_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = CS48L32_DSP_CLOCK1;
+ clk = &cs48l32_codec->dspclk;
+ clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val);
+ mask |= CS48L32_DSP_CLK_FREQ_MASK;
+ break;
+ case CS48L32_CLK_PDM_FLLCLK:
+ return cs48l32_set_pdm_fllclk(component, source);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (freq % 6144000)
+ val |= CS48L32_SYSCLK_FRAC_MASK;
+
+ dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, &reg);
+ if (ret != 0) {
+ cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & CS48L32_FLL_EN_MASK;
+}
+
+static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int val = 0;
+ int i;
+
+ cs48l32_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(regmap, fll->sts_addr, &val);
+ if (!!(val & fll->sts_mask) == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 6 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ fsleep(20000);
+ break;
+ }
+ }
+
+ cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock");
+
+ return -ETIMEDOUT;
+}
+
+static int cs48l32_fllhj_disable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ bool change;
+
+ cs48l32_fll_dbg(fll, "Disabling FLL\n");
+
+ /*
+ * Disable lockdet, but don't set ctrl_upd update bit. 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_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL5_OFFS,
+ CS48L32_FLL_FRC_INTEG_UPD_MASK);
+ regmap_update_bits_check(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK,
+ 0,
+ &change);
+
+ cs48l32_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_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ if (change)
+ pm_runtime_put_autosuspend(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
+ unsigned int gains, num;
+
+ cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++) {
+ if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH)
+ break;
+ }
+
+ fref = fin / (1 << refdiv);
+ fout = fll->fout;
+ frac = fout % fref;
+
+ /*
+ * Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ if (fref < CS48L32_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = CS48L32_FLLHJ_LOW_GAINS;
+
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < CS48L32_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_MID_GAINS;
+ fbdiv = (frac) ? 16 : 2;
+ } else {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 3;
+ min_n = CS48L32_FLLHJ_FRAC_MIN_N;
+ max_n = CS48L32_FLLHJ_FRAC_MAX_N;
+ } else {
+ if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)
+ hp = 0;
+ else
+ hp = 1;
+
+ min_n = CS48L32_FLLHJ_INT_MIN_N;
+ max_n = CS48L32_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < min_n) {
+ cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%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;
+
+ cs48l32_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) {
+ cs48l32_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)) {
+ cs48l32_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_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_THR_MASK |
+ CS48L32_FLL_PHASEDET_MASK |
+ CS48L32_FLL_REFCLK_DIV_MASK |
+ CS48L32_FLL_N_MASK |
+ CS48L32_FLL_CTRL_UPD_MASK,
+ (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) |
+ (1 << CS48L32_FLL_PHASEDET_SHIFT) |
+ (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) |
+ (fll_n << CS48L32_FLL_N_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL3_OFFS,
+ CS48L32_FLL_LAMBDA_MASK |
+ CS48L32_FLL_THETA_MASK,
+ (lambda << CS48L32_FLL_LAMBDA_SHIFT) |
+ (theta << CS48L32_FLL_THETA_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL4_OFFS,
+ (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ CS48L32_FLL_HP_MASK |
+ CS48L32_FLL_FB_DIV_MASK,
+ (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ (hp << CS48L32_FLL_HP_SHIFT) |
+ (fbdiv << CS48L32_FLL_FB_DIV_SHIFT));
+
+ return 0;
+}
+
+static int cs48l32_fllhj_enable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ int already_enabled = cs48l32_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(cs48l32->dev);
+
+ cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ /* Apply refclk */
+ ret = cs48l32_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_REFCLK_SRC_MASK,
+ fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK);
+
+out:
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ if (!already_enabled)
+ cs48l32_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_validate(struct cs48l32_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ cs48l32_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ cs48l32_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) {
+ cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ if (fout > CS48L32_FLL_MAX_FOUT) {
+ cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n",
+ fout, CS48L32_FLL_MAX_FOUT);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = cs48l32_fllhj_enable(fll);
+ else
+ cs48l32_fllhj_disable(fll);
+
+ return ret;
+}
+
+static int cs48l32_init_fll(struct cs48l32_fll *fll)
+{
+ fll->ref_src = CS48L32_FLL_SRC_NONE;
+
+ return 0;
+}
+
+static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case CS48L32_FLL1_REFCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout);
+}
+
+static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int pin_reg, last_pin_reg, hiz_reg;
+
+ switch (dai->id) {
+ case 1:
+ pin_reg = CS48L32_GPIO3_CTRL1;
+ hiz_reg = CS48L32_ASP1_CONTROL3;
+ break;
+ case 2:
+ pin_reg = CS48L32_GPIO7_CTRL1;
+ hiz_reg = CS48L32_ASP2_CONTROL3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg)
+ regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK);
+
+ /* DOUT high-impendance when not transmitting */
+ regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK);
+
+ return 0;
+}
+
+static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val = 0U;
+ unsigned int base = dai->driver->base;
+ unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK |
+ CS48L32_ASP_BCLK_MSTR_MASK |
+ CS48L32_ASP_FSYNC_INV_MASK |
+ CS48L32_ASP_FSYNC_MSTR_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported clock direction %d\n",
+ fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val);
+
+ return 0;
+}
+
+static const struct {
+ u32 freq;
+ u32 id;
+} cs48l32_sclk_rates[] = {
+ { 128000, 12 },
+ { 176400, 13 },
+ { 192000, 14 },
+ { 256000, 15 },
+ { 352800, 16 },
+ { 384000, 17 },
+ { 512000, 18 },
+ { 705600, 19 },
+ { 768000, 21 },
+ { 1024000, 23 },
+ { 1411200, 25 },
+ { 1536000, 27 },
+ { 2048000, 29 },
+ { 2822400, 31 },
+ { 3072000, 33 },
+ { 4096000, 36 },
+ { 5644800, 38 },
+ { 6144000, 40 },
+ { 8192000, 47 },
+ { 11289600, 49 },
+ { 12288000, 51 },
+ { 22579200, 57 },
+ { 24576000, 59 },
+};
+
+#define CS48L32_48K_RATE_MASK 0x0e00fe
+#define CS48L32_44K1_RATE_MASK 0x00fe00
+#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK)
+
+static const unsigned int cs48l32_sr_vals[] = {
+ 0,
+ 12000, /* CS48L32_48K_RATE_MASK */
+ 24000, /* CS48L32_48K_RATE_MASK */
+ 48000, /* CS48L32_48K_RATE_MASK */
+ 96000, /* CS48L32_48K_RATE_MASK */
+ 192000, /* CS48L32_48K_RATE_MASK */
+ 384000, /* CS48L32_48K_RATE_MASK */
+ 768000, /* CS48L32_48K_RATE_MASK */
+ 0,
+ 11025, /* CS48L32_44K1_RATE_MASK */
+ 22050, /* CS48L32_44K1_RATE_MASK */
+ 44100, /* CS48L32_44K1_RATE_MASK */
+ 88200, /* CS48L32_44K1_RATE_MASK */
+ 176400, /* CS48L32_44K1_RATE_MASK */
+ 352800, /* CS48L32_44K1_RATE_MASK */
+ 705600, /* CS48L32_44K1_RATE_MASK */
+ 0,
+ 8000, /* CS48L32_48K_RATE_MASK */
+ 16000, /* CS48L32_48K_RATE_MASK */
+ 32000, /* CS48L32_48K_RATE_MASK */
+};
+
+static const struct snd_pcm_hw_constraint_list cs48l32_constraint = {
+ .count = ARRAY_SIZE(cs48l32_sr_vals),
+ .list = cs48l32_sr_vals,
+};
+
+static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ base_rate = cs48l32_codec->sysclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ dai_priv->constraint.mask = CS48L32_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = CS48L32_48K_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int sr_val, sr_reg, rate;
+
+ rate = params_rate(params);
+ for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++)
+ if (cs48l32_sr_vals[sr_val] == rate)
+ break;
+
+ if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ sr_reg = CS48L32_SAMPLE_RATE1;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ sr_reg = CS48L32_SAMPLE_RATE2;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ sr_reg = CS48L32_SAMPLE_RATE3;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ sr_reg = CS48L32_SAMPLE_RATE4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val);
+
+ return 0;
+}
+
+static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component,
+ unsigned int base, unsigned int sclk,
+ unsigned int slotws, unsigned int dataw)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1);
+ if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1);
+ if (dataw != (val & (CS48L32_ASP_TX_WL_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5);
+ if (dataw != (val & (CS48L32_ASP_RX_WL_MASK)))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int base = dai->driver->base;
+ int dai_id = dai->id - 1;
+ unsigned int rate = params_rate(params);
+ unsigned int dataw = snd_pcm_format_width(params_format(params));
+ unsigned int asp_state = 0;
+ int sclk, sclk_target;
+ unsigned int slotw, n_slots, n_slots_multiple, val;
+ int i, ret;
+
+ cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n",
+ params_channels(params), dataw, rate);
+ /*
+ * The following calculations hold only under the assumption that
+ * symmetric_[rates|channels|samplebits] are set to 1
+ */
+ if (cs48l32_codec->tdm_slots[dai_id]) {
+ n_slots = cs48l32_codec->tdm_slots[dai_id];
+ slotw = cs48l32_codec->tdm_width[dai_id];
+ } else {
+ n_slots = params_channels(params);
+ slotw = dataw;
+ }
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT;
+ if (val == CS48L32_ASP_FMT_I2S_MODE)
+ n_slots_multiple = 2;
+ else
+ n_slots_multiple = 1;
+
+ sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple);
+ if (sclk_target < 0) {
+ cs48l32_asp_err(dai, "Invalid parameters\n");
+ return sclk_target;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) {
+ if ((cs48l32_sclk_rates[i].freq >= sclk_target) &&
+ (cs48l32_sclk_rates[i].freq % rate == 0)) {
+ sclk = cs48l32_sclk_rates[i].id;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(cs48l32_sclk_rates)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n",
+ n_slots, dataw, slotw, sclk_target, sclk);
+
+ slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) |
+ (slotw << CS48L32_ASP_RX_WIDTH_SHIFT);
+
+ if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw))
+ return cs48l32_hw_params_rate(substream, params, dai);
+
+ /* ASP must be disabled while changing configuration */
+ asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1);
+ regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff);
+
+ ret = cs48l32_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_asp;
+
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_BCLK_FREQ_MASK,
+ sclk);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL2,
+ CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK,
+ slotw);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_DATA_CONTROL1,
+ CS48L32_ASP_TX_WL_MASK,
+ dataw);
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_DATA_CONTROL5,
+ CS48L32_ASP_RX_WL_MASK,
+ dataw);
+
+restore_asp:
+ /* Restore ASP TX/RX enable state */
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_ENABLES1,
+ 0xff00ff,
+ asp_state);
+ return ret;
+}
+
+static const char *cs48l32_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ return "SYSCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base = dai->driver->base;
+ unsigned int current_asp_rate, target_asp_rate;
+ bool change_rate_domain = false;
+ int ret;
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ cs48l32_asp_err(dai, "Can't change clock on active DAI\n");
+ return -EBUSY;
+ }
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_priv->clk = clk_id;
+ cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id));
+
+ if (base) {
+ ret = regmap_read(cs48l32_codec->core.regmap,
+ base + CS48L32_ASP_CONTROL1,
+ &current_asp_rate);
+ if (ret != 0) {
+ cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((current_asp_rate & CS48L32_ASP_RATE_MASK) !=
+ (target_asp_rate & CS48L32_ASP_RATE_MASK)) {
+ change_rate_domain = true;
+
+ mutex_lock(&cs48l32_codec->rate_lock);
+ /* Guard the rate change with SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ }
+
+ snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_RATE_MASK, target_asp_rate);
+
+ if (change_rate_domain) {
+ cs48l32_spin_sysclk(cs48l32_codec);
+ mutex_unlock(&cs48l32_codec->rate_lock);
+ }
+ }
+
+ return 0;
+}
+
+static void cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int slot, i, j = 0, shift;
+ unsigned int frame_ctls[2] = {0, 0};
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ if (i - (j * 4) >= 4) {
+ ++j;
+ if (j >= 2)
+ break;
+ }
+
+ shift = (8 * (i - j * 4));
+
+ frame_ctls[j] |= slot << shift;
+
+ mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
+ }
+
+ regmap_write(regmap, base, frame_ctls[0]);
+ regmap_write(regmap, base + 0x4, frame_ctls[1]);
+
+ if (mask)
+ cs48l32_asp_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = 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 ASPs */
+ if (dai->id > CS48L32_MAX_ASP)
+ return -EINVAL;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1,
+ tx_max_chan, tx_mask);
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5,
+ rx_max_chan, rx_mask);
+
+ cs48l32_codec->tdm_width[dai->id - 1] = slot_width;
+ cs48l32_codec->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs48l32_dai_ops = {
+ .probe = &cs48l32_asp_dai_probe,
+ .startup = &cs48l32_startup,
+ .set_fmt = &cs48l32_set_fmt,
+ .set_tdm_slot = &cs48l32_set_tdm_slot,
+ .hw_params = &cs48l32_hw_params,
+ .set_sysclk = &cs48l32_dai_set_sysclk,
+};
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ return 0;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = CS48L32_IN1L_CONTROL2;
+ else
+ reg = CS48L32_IN1R_CONTROL2;
+
+ reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(200, 300);
+
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending--;
+ snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0);
+
+ /* Uncached write-only register, no need for update_bits */
+ if (!cs48l32_codec->in_up_pending) {
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK);
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Uncached write-only register, no need for update_bits.
+ * Will fail if codec is off but that will be handled by cs48l32_in_ev
+ */
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU);
+
+ return ret;
+}
+
+static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
+{
+ s16 a = be16_to_cpu(in_a);
+ s16 b = be16_to_cpu(in_b);
+
+ if (!mode)
+ return abs(a) > CS48L32_EQ_MAX_COEFF;
+
+ if (abs(b) > CS48L32_EQ_MAX_COEFF)
+ return true;
+
+ if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int mode = cs48l32_codec->eq_mode[w->shift];
+ unsigned int reg;
+ __be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0];
+ int ret = 0;
+
+ reg = CS48L32_EQ1_BAND1_COEFF1;
+ reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) ||
+ cs48l32_eq_filter_unstable(true, data[7], data[6]) ||
+ cs48l32_eq_filter_unstable(true, data[13], data[12]) ||
+ cs48l32_eq_filter_unstable(true, data[19], data[18]) ||
+ cs48l32_eq_filter_unstable(false, data[25], data[24])) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n");
+ ret = -EINVAL;
+ } else {
+ ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ coefficients: %d\n", ret);
+ goto out;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_EQ_CONTROL2,
+ w->mask,
+ mode << w->shift);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ mode: %d\n", ret);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs48l32_snd_controls[] = {
+SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp),
+
+CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]),
+CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]),
+
+SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]),
+SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]),
+
+SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+
+SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]),
+SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]),
+SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]),
+SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]),
+SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]),
+
+SOC_SINGLE("Ultrasonic 1 Detect LPF Switch",
+ CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+SOC_SINGLE("Ultrasonic 2 Detect LPF Switch",
+ CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+
+SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]),
+SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]),
+
+CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1),
+
+SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+
+CS48L32_EQ_COEFF_CONTROLS(EQ1),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ3),
+SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ4),
+SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1),
+
+SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+
+CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1),
+
+CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF),
+CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF),
+CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF),
+CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF),
+
+SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]),
+SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]),
+SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]),
+SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]),
+
+CS48L32_RATE_CONTROL("Sample Rate 1", 1),
+CS48L32_RATE_CONTROL("Sample Rate 2", 2),
+CS48L32_RATE_CONTROL("Sample Rate 3", 3),
+CS48L32_RATE_CONTROL("Sample Rate 4", 4),
+
+CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate),
+
+CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]),
+CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]),
+CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]),
+CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]),
+CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]),
+CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]),
+
+SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq),
+SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq),
+
+SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put),
+
+CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate),
+
+SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv),
+
+CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1),
+
+CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+
+CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0),
+CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1),
+CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2),
+CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3),
+CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4),
+CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5),
+CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6),
+CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7),
+CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8),
+CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9),
+CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10),
+CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11),
+CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12),
+CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13),
+CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14),
+CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15),
+};
+
+CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1);
+CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1);
+CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1);
+CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1);
+
+CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1);
+
+CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1);
+
+CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1);
+
+static int cs48l32_dsp_mem_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ case SND_SOC_DAPM_PRE_PMD:
+ cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0,
+ cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0),
+
+SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT,
+ 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1LN_1"),
+SND_SOC_DAPM_INPUT("IN1LN_2"),
+SND_SOC_DAPM_INPUT("IN1LP_1"),
+SND_SOC_DAPM_INPUT("IN1LP_2"),
+SND_SOC_DAPM_INPUT("IN1RN_1"),
+SND_SOC_DAPM_INPUT("IN1RN_2"),
+SND_SOC_DAPM_INPUT("IN1RP_1"),
+SND_SOC_DAPM_INPUT("IN1RP_2"),
+SND_SOC_DAPM_INPUT("IN1_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN1_PDMDATA"),
+
+SND_SOC_DAPM_INPUT("IN2_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN2_PDMDATA"),
+
+SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]),
+SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[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_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+
+SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0),
+
+SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0),
+
+SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]),
+SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[1]),
+
+SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]),
+SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in CS48L32_MIXER_INPUT_ROUTES
+ */
+SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0),
+
+SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, wm_adsp_early_event),
+
+/* end of ordered widget list */
+
+CS48L32_MIXER_WIDGETS(EQ1, "EQ1"),
+CS48L32_MIXER_WIDGETS(EQ2, "EQ2"),
+CS48L32_MIXER_WIDGETS(EQ3, "EQ3"),
+CS48L32_MIXER_WIDGETS(EQ4, "EQ4"),
+
+CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"),
+CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"),
+CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"),
+CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[1]),
+
+CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"),
+CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"),
+CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"),
+CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"),
+CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"),
+CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"),
+CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"),
+CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"),
+CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"),
+CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"),
+CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"),
+
+CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"),
+CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"),
+CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"),
+CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"),
+
+CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"),
+CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"),
+CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"),
+CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"),
+CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"),
+CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"),
+CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"),
+CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_dsp_trigger_output_mux[0]),
+
+SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+
+SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"),
+};
+
+static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = {
+ { "IN1LN_1", NULL, "SYSCLK" },
+ { "IN1LN_2", NULL, "SYSCLK" },
+ { "IN1LP_1", NULL, "SYSCLK" },
+ { "IN1LP_2", NULL, "SYSCLK" },
+ { "IN1RN_1", NULL, "SYSCLK" },
+ { "IN1RN_2", NULL, "SYSCLK" },
+ { "IN1RP_1", NULL, "SYSCLK" },
+ { "IN1RP_2", NULL, "SYSCLK" },
+
+ { "IN1_PDMCLK", NULL, "SYSCLK" },
+ { "IN1_PDMDATA", NULL, "SYSCLK" },
+ { "IN2_PDMCLK", NULL, "SYSCLK" },
+ { "IN2_PDMDATA", NULL, "SYSCLK" },
+
+ { "DSP1 Preloader", NULL, "DSP1MEM" },
+ { "DSP1", NULL, "DSP1FREQ" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Voice Ctrl DSP", NULL, "DSP1" },
+
+ { "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" },
+ { "MICBIAS1", NULL, "VOUT_MIC_REGULATED" },
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+ { "Noise Generator", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+ { "Noise Generator", NULL, "NOISE" },
+
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+ { "ASP1 Capture", NULL, "ASP1TX5" },
+ { "ASP1 Capture", NULL, "ASP1TX6" },
+ { "ASP1 Capture", NULL, "ASP1TX7" },
+ { "ASP1 Capture", NULL, "ASP1TX8" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "ASP1RX3", NULL, "ASP1 Playback" },
+ { "ASP1RX4", NULL, "ASP1 Playback" },
+ { "ASP1RX5", NULL, "ASP1 Playback" },
+ { "ASP1RX6", NULL, "ASP1 Playback" },
+ { "ASP1RX7", NULL, "ASP1 Playback" },
+ { "ASP1RX8", NULL, "ASP1 Playback" },
+
+ { "ASP2 Capture", NULL, "ASP2TX1" },
+ { "ASP2 Capture", NULL, "ASP2TX2" },
+ { "ASP2 Capture", NULL, "ASP2TX3" },
+ { "ASP2 Capture", NULL, "ASP2TX4" },
+
+ { "ASP2RX1", NULL, "ASP2 Playback" },
+ { "ASP2RX2", NULL, "ASP2 Playback" },
+ { "ASP2RX3", NULL, "ASP2 Playback" },
+ { "ASP2RX4", NULL, "ASP2 Playback" },
+
+ { "ASP1 Playback", NULL, "SYSCLK" },
+ { "ASP2 Playback", NULL, "SYSCLK" },
+
+ { "ASP1 Capture", NULL, "SYSCLK" },
+ { "ASP2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "Analog 1", "IN1LN_1" },
+ { "IN1L Mux", "Analog 2", "IN1LN_2" },
+ { "IN1L Mux", "Analog 1", "IN1LP_1" },
+ { "IN1L Mux", "Analog 2", "IN1LP_2" },
+ { "IN1R Mux", "Analog 1", "IN1RN_1" },
+ { "IN1R Mux", "Analog 2", "IN1RN_2" },
+ { "IN1R Mux", "Analog 1", "IN1RP_1" },
+ { "IN1R Mux", "Analog 2", "IN1RP_2" },
+
+ { "IN1L PGA", NULL, "IN1L Mode" },
+ { "IN1R PGA", NULL, "IN1R Mode" },
+
+ { "IN1L Mode", "Analog", "IN1L Mux" },
+ { "IN1R Mode", "Analog", "IN1R Mux" },
+
+ { "IN1L Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1L Mode", "Digital", "IN1_PDMDATA" },
+ { "IN1R Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1R Mode", "Digital", "IN1_PDMDATA" },
+
+ { "IN1L PGA", NULL, "VOUT_MIC" },
+ { "IN1R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "VOUT_MIC" },
+ { "IN2R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "IN2_PDMCLK" },
+ { "IN2R PGA", NULL, "IN2_PDMCLK" },
+ { "IN2L PGA", NULL, "IN2_PDMDATA" },
+ { "IN2R PGA", NULL, "IN2_PDMDATA" },
+
+ { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" },
+ { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" },
+
+ { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" },
+ { "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" },
+
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" },
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" },
+
+ CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"),
+ CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"),
+ CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"),
+ CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"),
+ CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"),
+ CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"),
+ CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"),
+ CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"),
+
+ CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"),
+ CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"),
+ CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"),
+ CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"),
+
+ CS48L32_MIXER_ROUTES("EQ1", "EQ1"),
+ CS48L32_MIXER_ROUTES("EQ2", "EQ2"),
+ CS48L32_MIXER_ROUTES("EQ3", "EQ3"),
+ CS48L32_MIXER_ROUTES("EQ4", "EQ4"),
+
+ CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"),
+ CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"),
+ CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"),
+ CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"),
+ CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"),
+ CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"),
+ CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" },
+ { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" },
+ { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" },
+
+ { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" },
+ { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" },
+ { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" },
+
+ { "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 cs48l32_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") &&
+ strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) {
+ dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&cs48l32_codec->dsp, stream);
+}
+
+static const struct snd_compress_ops cs48l32_compress_ops = {
+ .open = &cs48l32_compr_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_dai_ops cs48l32_compress_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs48l32_dai[] = {
+ {
+ .name = "cs48l32-asp1",
+ .id = 1,
+ .base = CS48L32_ASP1_ENABLES1,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-asp2",
+ .id = 2,
+ .base = CS48L32_ASP2_ENABLES1,
+ .playback = {
+ .stream_name = "ASP2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-cpu-trace",
+ .id = 3,
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-trace",
+ .id = 4,
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+ {
+ .name = "cs48l32-cpu-voicectrl",
+ .id = 5,
+ .capture = {
+ .stream_name = "Voice Ctrl CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-voicectrl",
+ .id = 6,
+ .capture = {
+ .stream_name = "Voice Ctrl DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+};
+
+static int cs48l32_init_inputs(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int ana_mode_l, ana_mode_r, dig_mode;
+ int i;
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ switch (cs48l32_codec->in_type[0][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ switch (cs48l32_codec->in_type[1][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n",
+ ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_l);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_r);
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT;
+
+ dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode);
+
+ regmap_update_bits(regmap,
+ CS48L32_INPUT1_CONTROL1 + (i * 0x40),
+ CS48L32_IN1_PDM_SUP_MASK, dig_mode);
+ }
+
+ return 0;
+}
+
+static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id)
+{
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id];
+
+ dai_priv->clk = CS48L32_CLK_SYSCLK_1;
+ dai_priv->constraint = cs48l32_constraint;
+
+ return 0;
+}
+
+static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode;
+ __be16 *data;
+ int i, ret;
+
+ ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1;
+
+ data = &cs48l32_codec->eq_coefficients[i][0];
+ ret = regmap_raw_read(regmap, reg + (i * 68), data,
+ CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error reading EQ coefficients: %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int cs48l32_component_probe(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap);
+
+ ret = cs48l32_init_inputs(component);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++)
+ cs48l32_init_dai(cs48l32_codec, i);
+
+ ret = cs48l32_init_eq(cs48l32_codec);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs48l32_codec->dsp, component);
+
+ /* Unmask DSP IRQs */
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ return 0;
+}
+
+static void cs48l32_component_remove(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ /* Mask DSP IRQs */
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ wm_adsp2_component_remove(&cs48l32_codec->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs48l32_soc_component_drv = {
+ .probe = &cs48l32_component_probe,
+ .remove = &cs48l32_component_remove,
+ .set_sysclk = &cs48l32_set_sysclk,
+ .set_pll = &cs48l32_set_fll,
+ .name = "cs48l32-codec",
+ .compress_ops = &cs48l32_compress_ops,
+ .controls = cs48l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs48l32_snd_controls),
+ .dapm_widgets = cs48l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets),
+ .dapm_routes = cs48l32_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec,
+ const char *propname,
+ u32 *dest,
+ int n_max)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ int ret;
+
+ ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max);
+ if (ret == -EINVAL)
+ return -ENOENT;
+
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname);
+
+ return 0;
+}
+
+static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,in-type";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS];
+ int i, in_idx, mux_way_idx, ret;
+
+ static_assert(ARRAY_SIZE(tmp) ==
+ ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0]));
+
+ ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+ if (ret < 0)
+ return;
+
+ in_idx = 0;
+ mux_way_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(tmp); ++i) {
+ switch (tmp[i]) {
+ case CS48L32_IN_TYPE_DIFF:
+ case CS48L32_IN_TYPE_SE:
+ cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, tmp[i]);
+ break;
+ }
+
+ /*
+ * Property array is [mux_way][in_channel]. Swap to
+ * [in_channel][mux_way] for convenience.
+ */
+ if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) {
+ in_idx = 0;
+ ++mux_way_idx;
+ }
+ }
+}
+
+static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,pdm-sup";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT];
+ int i;
+
+ static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup));
+
+ cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ switch (tmp[i]) {
+ case CS48L32_PDM_SUP_VOUT_MIC:
+ case CS48L32_PDM_SUP_MICBIAS1:
+ cs48l32_codec->pdm_sup[i] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, cs48l32_codec->pdm_sup[i]);
+ break;
+ }
+ }
+}
+
+static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec)
+{
+ cs48l32_prop_get_in_type(cs48l32_codec);
+ cs48l32_prop_get_pdm_sup(cs48l32_codec);
+}
+
+static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec)
+{
+ int irq = cs48l32_codec->core.irq;
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ /*
+ * Don't use devm because this must be freed before destroying the
+ * rest of the driver
+ */
+ ret = request_threaded_irq(irq, NULL, cs48l32_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs48l32", cs48l32_codec);
+ if (ret)
+ return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n");
+
+ return 0;
+}
+
+static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0);
+ static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai));
+
+ cs48l32_handle_properties(cs48l32_codec);
+
+ dsp = &cs48l32_codec->dsp;
+ dsp->part = "cs48l32";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs48l32_codec->core.dev;
+ dsp->cs_dsp.regmap = cs48l32_codec->core.regmap;
+ dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs48l32_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions);
+ dsp->pre_run = cs48l32_dsp_pre_run;
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0)
+ return ret;
+
+ cs48l32_codec->fll.codec = cs48l32_codec;
+ cs48l32_codec->fll.id = 1;
+ cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1;
+ cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6;
+ cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK;
+ cs48l32_init_fll(&cs48l32_codec->fll);
+
+ ret = cs48l32_request_interrupt(cs48l32_codec);
+ if (ret)
+ goto err_dsp;
+
+ ret = devm_snd_soc_register_component(cs48l32_codec->core.dev,
+ &cs48l32_soc_component_drv,
+ cs48l32_dai,
+ ARRAY_SIZE(cs48l32_dai));
+ if (ret < 0) {
+ dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n");
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(&cs48l32_codec->dsp);
+
+ return ret;
+}
+
+static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val,
+ ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)),
+ 1000, CS48L32_BOOT_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs48l32->dev, "BOOT_DONE timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret);
+ return ret;
+ }
+
+ if (val & BIT(CS48L32_MCU_STS_SHIFT)) {
+ dev_err(cs48l32->dev, "MCU boot failed\n");
+ return -EIO;
+ }
+
+ pm_runtime_mark_last_busy(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_soft_reset(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC);
+ if (ret != 0) {
+ dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ return 0;
+}
+
+static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio)
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0);
+}
+
+static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio) {
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1);
+ usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000);
+ }
+}
+
+static int cs48l32_runtime_resume(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret;
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret)
+ goto err;
+
+ /* Check whether registers reset during suspend */
+ regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val);
+ if (!val)
+ regcache_mark_dirty(cs48l32->regmap);
+ else
+ dev_dbg(cs48l32->dev, "Did not reset during suspend\n");
+
+ ret = regcache_sync(cs48l32->regmap);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return ret;
+}
+
+static int cs48l32_runtime_suspend(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Flag to detect if the registers reset during suspend */
+ regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1);
+
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs48l32_pm_ops = {
+ RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL)
+};
+
+static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(cs48l32->mclk1);
+ if (ret)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n");
+
+ ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1);
+ if (ret) {
+ clk_disable_unprepare(cs48l32->mclk1);
+ return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n");
+ }
+
+ return 0;
+}
+
+static int cs48l32_get_clocks(struct cs48l32 *cs48l32)
+{
+ cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1");
+ if (IS_ERR(cs48l32->mclk1))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1),
+ "Failed to get mclk1\n");
+
+ return 0;
+}
+
+static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32)
+{
+ struct gpio_desc *reset;
+
+ reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n");
+
+ /* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */
+ gpiod_set_raw_value_cansleep(reset, 0);
+
+ cs48l32->reset_gpio = reset;
+
+ return 0;
+}
+
+static int cs48l32_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cs48l32_codec *cs48l32_codec;
+ struct cs48l32 *cs48l32;
+ unsigned int hwid, rev, otp_rev;
+ int i, ret;
+
+ cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL);
+ if (!cs48l32_codec)
+ return -ENOMEM;
+
+ cs48l32 = &cs48l32_codec->core;
+ cs48l32->dev = dev;
+ cs48l32->irq = spi->irq;
+ mutex_init(&cs48l32_codec->rate_lock);
+ cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3;
+
+ dev_set_drvdata(cs48l32->dev, cs48l32_codec);
+
+ ret = cs48l32_create_regmap(spi, cs48l32);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n");
+
+ regcache_cache_only(cs48l32->regmap, true);
+
+ ret = cs48l32_get_reset_gpio(cs48l32);
+ if (ret)
+ return ret;
+
+ ret = cs48l32_get_clocks(cs48l32);
+ if (ret)
+ return ret;
+
+ static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies));
+ for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++)
+ cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies),
+ cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request core supplies\n");
+
+ cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d");
+ if (IS_ERR(cs48l32->vdd_d))
+ return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable core supplies\n");
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(dev, "Failed to enable vdd-d: %d\n", ret);
+ goto err_enable;
+ }
+
+ cs48l32_disable_hard_reset(cs48l32);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ /* If we don't have a reset GPIO use a soft reset */
+ if (!cs48l32->reset_gpio) {
+ ret = cs48l32_soft_reset(cs48l32);
+ if (ret)
+ goto err_reset;
+ }
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid);
+ if (ret) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ hwid &= CS48L32_DEVID_MASK;
+
+ switch (hwid) {
+ case CS48L32_SILICON_ID:
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev);
+ if (ret) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ goto err_reset;
+ }
+ rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK;
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev);
+ if (ret) {
+ dev_err(dev, "Failed to read OTP revision register: %d\n", ret);
+ goto err_reset;
+ }
+ otp_rev &= CS48L32_OTPID_MASK;
+
+ dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff,
+ rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev);
+
+ /* Apply hardware patch */
+ ret = cs48l32_apply_patch(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret);
+ goto err_reset;
+ }
+
+ /* BOOT_DONE interrupt is unmasked by default, so mask it */
+ ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK);
+
+ ret = cs48l32_configure_clk32k(cs48l32);
+ if (ret)
+ goto err_reset;
+
+ pm_runtime_set_active(cs48l32->dev);
+ pm_runtime_set_autosuspend_delay(cs48l32->dev, 100);
+ pm_runtime_use_autosuspend(cs48l32->dev);
+ pm_runtime_enable(cs48l32->dev);
+
+ ret = cs48l32_create_codec_component(cs48l32_codec);
+ if (ret)
+ goto err_clk32k;
+
+ return 0;
+
+err_clk32k:
+ clk_disable_unprepare(cs48l32->mclk1);
+err_reset:
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_disable(cs48l32->vdd_d);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ return ret;
+}
+
+static void cs48l32_spi_remove(struct spi_device *spi)
+{
+ struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Remove IRQ handler before destroying anything else */
+ if (cs48l32->irq >= 1)
+ free_irq(cs48l32->irq, cs48l32_codec);
+
+ pm_runtime_disable(cs48l32->dev);
+ regulator_disable(cs48l32->vdd_d);
+ clk_disable_unprepare(cs48l32->mclk1);
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ mutex_destroy(&cs48l32_codec->rate_lock);
+}
+
+static const struct of_device_id cs48l32_of_match[] = {
+ { .compatible = "cirrus,cs48l32", },
+ {},
+};
+
+static const struct spi_device_id cs48l32_spi_ids[] = {
+ { "cs48l32", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids);
+
+static struct spi_driver cs48l32_spi_driver = {
+ .driver = {
+ .name = "cs48l32",
+ .pm = pm_ptr(&cs48l32_pm_ops),
+ .of_match_table = cs48l32_of_match,
+ },
+ .probe = &cs48l32_spi_probe,
+ .remove = &cs48l32_spi_remove,
+ .id_table = cs48l32_spi_ids,
+};
+module_spi_driver(cs48l32_spi_driver);
+
+MODULE_DESCRIPTION("CS48L32 ASoC codec driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h
new file mode 100644
index 000000000000..c1b4e13feae4
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic CS48L32 audio DSP.
+ *
+ * Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+#ifndef SND_SOC_CS48L32_H
+#define SND_SOC_CS48L32_H
+
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include "wm_adsp.h"
+
+#define CS48L32_SILICON_ID 0x48a32
+
+#define CS48L32_32K_MCLK1 0
+
+#define CS48L32_SFT_RESET_MAGIC 0x5a000000
+#define CS48L32_SOFT_RESET_US 2000
+#define CS48L32_HARD_RESET_MIN_US 1000
+
+#define CS48L32_SEEN_BOOT_DONE BIT(0)
+#define CS48L32_BOOT_TIMEOUT_US 25000
+
+#define CS48L32_ASP_ENABLES1 0x00
+#define CS48L32_ASP_CONTROL1 0x04
+#define CS48L32_ASP_CONTROL2 0x08
+#define CS48L32_ASP_CONTROL3 0x0c
+#define CS48L32_ASP_FRAME_CONTROL1 0x10
+#define CS48L32_ASP_FRAME_CONTROL2 0x14
+#define CS48L32_ASP_FRAME_CONTROL5 0x20
+#define CS48L32_ASP_FRAME_CONTROL6 0x24
+#define CS48L32_ASP_DATA_CONTROL1 0x30
+#define CS48L32_ASP_DATA_CONTROL5 0x40
+#define CS48L32_SYSCLK_RATE_6MHZ 0
+#define CS48L32_SYSCLK_RATE_12MHZ 1
+#define CS48L32_SYSCLK_RATE_24MHZ 2
+#define CS48L32_SYSCLK_RATE_49MHZ 3
+#define CS48L32_SYSCLK_RATE_98MHZ 4
+#define CS48L32_FLLHJ_INT_MAX_N 1023
+#define CS48L32_FLLHJ_INT_MIN_N 1
+#define CS48L32_FLLHJ_FRAC_MAX_N 255
+#define CS48L32_FLLHJ_FRAC_MIN_N 2
+#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000
+#define CS48L32_FLLHJ_LOW_THRESH 192000
+#define CS48L32_FLLHJ_MID_THRESH 1152000
+#define CS48L32_FLLHJ_MAX_THRESH 13000000
+#define CS48L32_FLLHJ_LOW_GAINS 0x23f0
+#define CS48L32_FLLHJ_MID_GAINS 0x22f2
+#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0
+#define CS48L32_FLL_MAX_FOUT 50000000
+#define CS48L32_FLL_MAX_REFDIV 8
+#define CS48L32_FLL_CONTROL1_OFFS 0x00
+#define CS48L32_FLL_CONTROL2_OFFS 0x04
+#define CS48L32_FLL_CONTROL3_OFFS 0x08
+#define CS48L32_FLL_CONTROL4_OFFS 0x0c
+#define CS48L32_FLL_CONTROL5_OFFS 0x10
+#define CS48L32_FLL_CONTROL6_OFFS 0x14
+#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34
+#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0
+#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000
+#define CS48L32_ASP_FMT_DSP_MODE_A 0
+#define CS48L32_ASP_FMT_DSP_MODE_B 1
+#define CS48L32_ASP_FMT_I2S_MODE 2
+#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3
+#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080
+#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280
+#define CS48L32_HALO_DSP_RATE_MASK 0x1f
+
+#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0
+#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1
+#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2
+#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3
+#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8
+#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9
+
+#define CS48L32_MAX_DAI 6
+#define CS48L32_MAX_INPUT 4
+#define CS48L32_MAX_ANALOG_INPUT 2
+#define CS48L32_MAX_IN_MUX_WAYS 2
+#define CS48L32_MAX_ASP 2
+
+#define CS48L32_EQ_BLOCK_SZ 60
+#define CS48L32_N_EQ_BLOCKS 4
+
+#define CS48L32_DSP_N_RX_CHANNELS 8
+#define CS48L32_DSP_N_TX_CHANNELS 8
+
+#define CS48L32_LHPF_MAX_COEFF 4095
+#define CS48L32_EQ_MAX_COEFF 4095
+
+#define CS48L32_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv)
+
+#define CS48L32_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL( \
+ name, reg, 0, CS48L32_MIXER_SRC_MASK, \
+ cs48l32_mixer_texts, cs48l32_mixer_values)
+
+#define CS48L32_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum)
+
+#define CS48L32_MUX_ENUMS(name, base_reg) \
+ static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static CS48L32_MUX_CTL_DECL(name)
+
+#define CS48L32_MIXER_ENUMS(name, base_reg) \
+ CS48L32_MUX_ENUMS(name##_in1, base_reg); \
+ CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \
+ CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \
+ CS48L32_MUX_ENUMS(name##_in4, base_reg + 12)
+
+#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux)
+
+#define CS48L32_MIXER_WIDGETS(name, name_str) \
+ CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \
+ CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \
+ CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \
+ CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS48L32_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define CS48L32_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" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+ { name, NULL, name " Preloader" }, \
+ { name, NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader" }, \
+ CS48L32_MIXER_ROUTES(name, name "RX1"), \
+ CS48L32_MIXER_ROUTES(name, name "RX2"), \
+ CS48L32_MIXER_ROUTES(name, name "RX3"), \
+ CS48L32_MIXER_ROUTES(name, name "RX4"), \
+ CS48L32_MIXER_ROUTES(name, name "RX5"), \
+ CS48L32_MIXER_ROUTES(name, name "RX6"), \
+ CS48L32_MIXER_ROUTES(name, name "RX7"), \
+ CS48L32_MIXER_ROUTES(name, name "RX8") \
+
+#define CS48L32_DSP_ROUTES_1_8(name) \
+ { name, NULL, "DSPCLK" }, \
+ CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+
+#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1])
+
+#define CS48L32_RATE_ENUM(name, enum) \
+ SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put)
+
+#define CS48L32_DSP_RATE_CONTROL(name, num) \
+ SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \
+ cs48l32_dsp_rate_get, cs48l32_dsp_rate_put)
+
+#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \
+ .put = cs48l32_eq_coeff_put, .private_value = \
+ (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\
+ .shift = xshift, .block_base = xbase, .max = 65535 } }
+
+#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \
+ CS48L32_ ## eq ## _ ## band ## _ ## type
+
+#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_EQ_COEFF_CONTROLS(name) \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = cs48l32_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */
+#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \
+ SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \
+ event, SND_SOC_DAPM_POST_PMU)
+
+#define CS48L32_RATES SNDRV_PCM_RATE_KNOT
+
+#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS48L32_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "ASP1RX1", "ASP1RX1" }, \
+ { name, "ASP1RX2", "ASP1RX2" }, \
+ { name, "ASP1RX3", "ASP1RX3" }, \
+ { name, "ASP1RX4", "ASP1RX4" }, \
+ { name, "ASP1RX5", "ASP1RX5" }, \
+ { name, "ASP1RX6", "ASP1RX6" }, \
+ { name, "ASP1RX7", "ASP1RX7" }, \
+ { name, "ASP1RX8", "ASP1RX8" }, \
+ { name, "ASP2RX1", "ASP2RX1" }, \
+ { name, "ASP2RX2", "ASP2RX2" }, \
+ { name, "ASP2RX3", "ASP2RX3" }, \
+ { name, "ASP2RX4", "ASP2RX4" }, \
+ { 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" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { 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, "Ultrasonic 1", "Ultrasonic 1" }, \
+ { name, "Ultrasonic 2", "Ultrasonic 2" }, \
+ { 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, "DSP1.7", "DSP1" }, \
+ { name, "DSP1.8", "DSP1" }
+
+struct cs48l32_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+struct cs48l32_eq_control {
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int block_base;
+ unsigned int max;
+};
+
+struct cs48l32_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct cs48l32_dsp_power_reg_block {
+ unsigned int start;
+ unsigned int end;
+};
+
+struct cs48l32_dsp_power_regs {
+ const unsigned int *pwd;
+ unsigned int n_pwd;
+ const struct cs48l32_dsp_power_reg_block *ext;
+ unsigned int n_ext;
+};
+
+struct cs48l32;
+struct cs48l32_codec;
+struct spi_device;
+
+struct cs48l32_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct cs48l32_fll {
+ struct cs48l32_codec *codec;
+ int id;
+ unsigned int base;
+
+ unsigned int sts_addr;
+ unsigned int sts_mask;
+ unsigned int fout;
+ int ref_src;
+ unsigned int ref_freq;
+
+ struct cs48l32_fll_cfg ref_cfg;
+};
+
+struct cs48l32_codec {
+ struct wm_adsp dsp; /* must be first */
+ struct cs48l32 core;
+ int sysclk;
+ int dspclk;
+ struct cs48l32_dai_priv dai[CS48L32_MAX_DAI];
+ struct cs48l32_fll fll;
+
+ unsigned int in_up_pending;
+ unsigned int in_vu_reg;
+
+ struct mutex rate_lock;
+
+ u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS];
+
+ u8 in_type[CS48L32_MAX_ANALOG_INPUT][CS48L32_MAX_IN_MUX_WAYS];
+ u8 pdm_sup[CS48L32_MAX_ANALOG_INPUT];
+ u8 tdm_width[CS48L32_MAX_ASP];
+ u8 tdm_slots[CS48L32_MAX_ASP];
+
+ unsigned int eq_mode[CS48L32_N_EQ_BLOCKS];
+ __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2];
+
+ const struct cs48l32_dsp_power_regs *dsp_power_regs;
+};
+
+#define cs48l32_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define cs48l32_asp_err(_dai, fmt, ...) \
+ dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_warn(_dai, fmt, ...) \
+ dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32);
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32);
+int cs48l32_enable_asp1_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_enable_asp2_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_micvdd_voltage_index(u32 voltage);
+int cs48l32_micbias1_voltage_index(u32 voltage);
+
+#endif
diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c
new file mode 100644
index 000000000000..22b1a4d6b61c
--- /dev/null
+++ b/sound/soc/codecs/cs530x-i2c.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "cs530x.h"
+
+static const struct of_device_id cs530x_of_match[] = {
+ {
+ .compatible = "cirrus,cs5302",
+ .data = (void *)CS5302,
+ }, {
+ .compatible = "cirrus,cs5304",
+ .data = (void *)CS5304,
+ }, {
+ .compatible = "cirrus,cs5308",
+ .data = (void *)CS5308,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, cs530x_of_match);
+
+static const struct i2c_device_id cs530x_i2c_id[] = {
+ { "cs5302", CS5302 },
+ { "cs5304", CS5304 },
+ { "cs5308", CS5308 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id);
+
+static int cs530x_i2c_probe(struct i2c_client *client)
+{
+ struct cs530x_priv *cs530x;
+
+ cs530x = devm_kzalloc(&client->dev, sizeof(*cs530x), GFP_KERNEL);
+ if (!cs530x)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, cs530x);
+
+ cs530x->regmap = devm_regmap_init_i2c(client, &cs530x_regmap);
+ if (IS_ERR(cs530x->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(cs530x->regmap),
+ "Failed to allocate register map\n");
+
+ cs530x->devtype = (uintptr_t)i2c_get_match_data(client);
+ cs530x->dev = &client->dev;
+
+ return cs530x_probe(cs530x);
+}
+
+static struct i2c_driver cs530x_i2c_driver = {
+ .driver = {
+ .name = "cs530x",
+ .of_match_table = cs530x_of_match,
+ },
+ .probe = cs530x_i2c_probe,
+ .id_table = cs530x_i2c_id,
+};
+module_i2c_driver(cs530x_i2c_driver);
+
+MODULE_DESCRIPTION("I2C CS530X driver");
+MODULE_IMPORT_NS("SND_SOC_CS530X");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
new file mode 100644
index 000000000000..b9eff240b929
--- /dev/null
+++ b/sound/soc/codecs/cs530x.c
@@ -0,0 +1,971 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS530x CODEC driver
+//
+// Copyright (C) 2024 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs530x.h"
+
+#define CS530X_MAX_ADC_CH 8
+#define CS530X_MIN_ADC_CH 2
+
+static const char *cs530x_supply_names[CS530X_NUM_SUPPLIES] = {
+ "vdd-a",
+ "vdd-io",
+};
+
+static const struct reg_default cs530x_reg_defaults[] = {
+ { CS530X_CLK_CFG_0, 0x30 },
+ { CS530X_CLK_CFG_1, 0x0001 },
+ { CS530X_CHIP_ENABLE, 0 },
+ { CS530X_ASP_CFG, 0 },
+ { CS530X_SIGNAL_PATH_CFG, 0 },
+ { CS530X_IN_ENABLES, 0 },
+ { CS530X_IN_RAMP_SUM, 0x0022 },
+ { CS530X_IN_FILTER, 0 },
+ { CS530X_IN_HIZ, 0 },
+ { CS530X_IN_INV, 0 },
+ { CS530X_IN_VOL_CTRL1_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL1_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL2_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL3_1, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_0, 0x8000 },
+ { CS530X_IN_VOL_CTRL4_1, 0x8000 },
+ { CS530X_PAD_FN, 0 },
+ { CS530X_PAD_LVL, 0 },
+};
+
+static bool cs530x_read_and_write_regs(unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_CLK_CFG_0:
+ case CS530X_CLK_CFG_1:
+ case CS530X_CHIP_ENABLE:
+ case CS530X_ASP_CFG:
+ case CS530X_SIGNAL_PATH_CFG:
+ case CS530X_IN_ENABLES:
+ case CS530X_IN_RAMP_SUM:
+ case CS530X_IN_FILTER:
+ case CS530X_IN_HIZ:
+ case CS530X_IN_INV:
+ case CS530X_IN_VOL_CTRL1_0:
+ case CS530X_IN_VOL_CTRL1_1:
+ case CS530X_IN_VOL_CTRL2_0:
+ case CS530X_IN_VOL_CTRL2_1:
+ case CS530X_IN_VOL_CTRL3_0:
+ case CS530X_IN_VOL_CTRL3_1:
+ case CS530X_IN_VOL_CTRL4_0:
+ case CS530X_IN_VOL_CTRL4_1:
+ case CS530X_PAD_FN:
+ case CS530X_PAD_LVL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs530x_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_DEVID:
+ case CS530X_REVID:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static bool cs530x_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS530X_SW_RESET:
+ case CS530X_IN_VOL_CTRL5:
+ return true;
+ default:
+ return cs530x_read_and_write_regs(reg);
+ }
+}
+
+static int cs530x_put_volsw_vu(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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret)
+ goto volsw_err;
+
+ /* Write IN_VU bit for the volume change to take effect */
+ regmap_write(regmap, CS530X_IN_VOL_CTRL5, CS530X_IN_VU);
+
+volsw_err:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1270, 50, 0);
+
+static const char * const cs530x_in_filter_text[] = {
+ "Min Phase Slow Roll-off",
+ "Min Phase Fast Roll-off",
+ "Linear Phase Slow Roll-off",
+ "Linear Phase Fast Roll-off",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_filter_enum, CS530X_IN_FILTER,
+ CS530X_IN_FILTER_SHIFT,
+ cs530x_in_filter_text);
+
+static const char * const cs530x_in_4ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch4_enum, CS530X_IN_RAMP_SUM,
+ CS530X_IN_SUM_MODE_SHIFT,
+ cs530x_in_4ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_4ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch4_enum),
+};
+
+static const char * const cs530x_in_8ch_sum_text[] = {
+ "None",
+ "Groups of 2",
+ "Groups of 4",
+ "Groups of 8",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_in_sum_ch8_enum, CS530X_IN_RAMP_SUM,
+ CS530X_IN_SUM_MODE_SHIFT,
+ cs530x_in_8ch_sum_text);
+
+static const struct snd_kcontrol_new cs530x_in_sum_8ch_controls[] = {
+SOC_ENUM("IN Sum Select", cs530x_in_sum_ch8_enum),
+};
+
+
+static const char * const cs530x_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_inc_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_INC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs530x_ramp_dec_enum, CS530X_IN_RAMP_SUM,
+ CS530X_RAMP_RATE_DEC_SHIFT,
+ cs530x_vol_ramp_text);
+
+static const struct snd_kcontrol_new cs530x_in_1_to_2_controls[] = {
+SOC_SINGLE_EXT_TLV("IN1 Volume", CS530X_IN_VOL_CTRL1_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN2 Volume", CS530X_IN_VOL_CTRL1_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_ENUM("IN DEC Filter Select", cs530x_in_filter_enum),
+SOC_ENUM("Input Ramp Up", cs530x_ramp_inc_enum),
+SOC_ENUM("Input Ramp Down", cs530x_ramp_dec_enum),
+
+SOC_SINGLE("ADC1 Invert Switch", CS530X_IN_INV, CS530X_IN1_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC2 Invert Switch", CS530X_IN_INV, CS530X_IN2_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_3_to_4_controls[] = {
+SOC_SINGLE_EXT_TLV("IN3 Volume", CS530X_IN_VOL_CTRL2_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN4 Volume", CS530X_IN_VOL_CTRL2_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC3 Invert Switch", CS530X_IN_INV, CS530X_IN3_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC4 Invert Switch", CS530X_IN_INV, CS530X_IN4_INV_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs530x_in_5_to_8_controls[] = {
+SOC_SINGLE_EXT_TLV("IN5 Volume", CS530X_IN_VOL_CTRL3_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN6 Volume", CS530X_IN_VOL_CTRL3_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN7 Volume", CS530X_IN_VOL_CTRL4_0, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+SOC_SINGLE_EXT_TLV("IN8 Volume", CS530X_IN_VOL_CTRL4_1, 0, 255, 1,
+ snd_soc_get_volsw, cs530x_put_volsw_vu, in_vol_tlv),
+
+SOC_SINGLE("ADC5 Invert Switch", CS530X_IN_INV, CS530X_IN5_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC6 Invert Switch", CS530X_IN_INV, CS530X_IN6_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC7 Invert Switch", CS530X_IN_INV, CS530X_IN7_INV_SHIFT, 1, 0),
+SOC_SINGLE("ADC8 Invert Switch", CS530X_IN_INV, CS530X_IN8_INV_SHIFT, 1, 0),
+};
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ cs530x->adc_pairs_count++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_IN_MUTE);
+ regmap_clear_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift+1) * 2), CS530X_IN_MUTE);
+
+ cs530x->adc_pairs_count--;
+ if (!cs530x->adc_pairs_count) {
+ usleep_range(1000, 1100);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_IN_VU);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ (w->shift * 2), CS530X_IN_MUTE);
+ regmap_set_bits(regmap, CS530X_IN_VOL_CTRL1_0 +
+ ((w->shift+1) * 2), CS530X_IN_MUTE);
+ return regmap_write(regmap, CS530X_IN_VOL_CTRL5,
+ CS530X_IN_VU);
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new adc12_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc34_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc56_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new adc78_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+static const struct snd_kcontrol_new in_hpf_ctrl =
+ SOC_DAPM_SINGLE_VIRT("Switch", 1);
+
+/* General DAPM widgets for all devices */
+static const struct snd_soc_dapm_widget cs530x_gen_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Global Enable", CS530X_CHIP_ENABLE, 0, 0, NULL, 0),
+};
+
+/* ADC's Channels 1 and 2 plus generic ADC DAPM events */
+static const struct snd_soc_dapm_widget cs530x_adc_ch12_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+SND_SOC_DAPM_ADC_E("ADC1", NULL, CS530X_IN_ENABLES, 0, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC2", NULL, CS530X_IN_ENABLES, 1, 0),
+SND_SOC_DAPM_SWITCH("ADC12 Enable", SND_SOC_NOPM, 0, 0, &adc12_ctrl),
+SND_SOC_DAPM_SWITCH("IN HPF", CS530X_IN_FILTER, CS530X_IN_HPF_EN_SHIFT,
+ 0, &in_hpf_ctrl),
+};
+
+/* ADC's Channels 3 and 4 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch34_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN3"),
+SND_SOC_DAPM_INPUT("IN4"),
+SND_SOC_DAPM_ADC_E("ADC3", NULL, CS530X_IN_ENABLES, 2, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC4", NULL, CS530X_IN_ENABLES, 3, 0),
+SND_SOC_DAPM_SWITCH("ADC34 Enable", SND_SOC_NOPM, 0, 0, &adc34_ctrl),
+};
+
+/* ADC's Channels 5 to 8 */
+static const struct snd_soc_dapm_widget cs530x_adc_ch58_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN5"),
+SND_SOC_DAPM_INPUT("IN6"),
+SND_SOC_DAPM_INPUT("IN7"),
+SND_SOC_DAPM_INPUT("IN8"),
+SND_SOC_DAPM_ADC_E("ADC5", NULL, CS530X_IN_ENABLES, 4, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC6", NULL, CS530X_IN_ENABLES, 5, 0),
+SND_SOC_DAPM_SWITCH("ADC56 Enable", SND_SOC_NOPM, 0, 0, &adc56_ctrl),
+SND_SOC_DAPM_ADC_E("ADC7", NULL, CS530X_IN_ENABLES, 6, 0,
+ cs530x_adc_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_ADC("ADC8", NULL, CS530X_IN_ENABLES, 7, 0),
+SND_SOC_DAPM_SWITCH("ADC78 Enable", SND_SOC_NOPM, 0, 0, &adc78_ctrl),
+};
+
+static const struct snd_soc_dapm_route adc_ch1_2_routes[] = {
+ { "ADC1", NULL, "Global Enable" },
+ { "ADC2", NULL, "Global Enable" },
+
+ { "ADC12 Enable", "Switch", "IN1" },
+ { "ADC12 Enable", "Switch", "IN2" },
+ { "ADC1", NULL, "ADC12 Enable" },
+ { "ADC2", NULL, "ADC12 Enable" },
+ { "IN HPF", "Switch", "ADC1" },
+ { "IN HPF", "Switch", "ADC2" },
+
+ { "AIF Capture", NULL, "IN HPF" },
+ { "AIF Capture", NULL, "ADC1" },
+ { "AIF Capture", NULL, "ADC2" },
+};
+
+static const struct snd_soc_dapm_route adc_ch3_4_routes[] = {
+ { "ADC3", NULL, "Global Enable" },
+ { "ADC4", NULL, "Global Enable" },
+
+ { "ADC34 Enable", "Switch", "IN3" },
+ { "ADC34 Enable", "Switch", "IN4" },
+ { "ADC3", NULL, "ADC34 Enable" },
+ { "ADC4", NULL, "ADC34 Enable" },
+ { "IN HPF", "Switch", "ADC3" },
+ { "IN HPF", "Switch", "ADC4" },
+
+ { "AIF Capture", NULL, "ADC3" },
+ { "AIF Capture", NULL, "ADC4" },
+};
+
+static const struct snd_soc_dapm_route adc_ch5_8_routes[] = {
+ { "ADC5", NULL, "Global Enable" },
+ { "ADC6", NULL, "Global Enable" },
+ { "ADC7", NULL, "Global Enable" },
+ { "ADC8", NULL, "Global Enable" },
+
+ { "ADC56 Enable", "Switch", "IN5" },
+ { "ADC56 Enable", "Switch", "IN6" },
+ { "ADC5", NULL, "ADC56 Enable" },
+ { "ADC6", NULL, "ADC56 Enable" },
+ { "IN HPF", "Switch", "ADC5" },
+ { "IN HPF", "Switch", "ADC6" },
+
+ { "AIF Capture", NULL, "ADC5" },
+ { "AIF Capture", NULL, "ADC6" },
+
+ { "ADC78 Enable", "Switch", "IN7" },
+ { "ADC78 Enable", "Switch", "IN8" },
+ { "ADC7", NULL, "ADC78 Enable" },
+ { "ADC8", NULL, "ADC78 Enable" },
+ { "IN HPF", "Switch", "ADC7" },
+ { "IN HPF", "Switch", "ADC8" },
+
+ { "AIF Capture", NULL, "ADC7" },
+ { "AIF Capture", NULL, "ADC8" },
+};
+
+static void cs530x_add_12_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_1_to_2_controls,
+ ARRAY_SIZE(cs530x_in_1_to_2_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch12_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch12_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch1_2_routes,
+ ARRAY_SIZE(adc_ch1_2_routes));
+}
+
+static void cs530x_add_34_adc_widgets(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+ snd_soc_add_component_controls(component,
+ cs530x_in_3_to_4_controls,
+ ARRAY_SIZE(cs530x_in_3_to_4_controls));
+
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch34_dapm_widgets,
+ ARRAY_SIZE(cs530x_adc_ch34_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, adc_ch3_4_routes,
+ ARRAY_SIZE(adc_ch3_4_routes));
+}
+
+static int cs530x_set_bclk(struct snd_soc_component *component, const int freq)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int bclk_val;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ bclk_val = CS530X_BCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ bclk_val = CS530X_BCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ bclk_val = CS530X_BCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ bclk_val = CS530X_BCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid BCLK frequency %d\n", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(component->dev, "BCLK frequency is %d\n", freq);
+
+ return regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_BCLK_FREQ_MASK, bclk_val);
+}
+
+static int cs530x_set_pll_refclk(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int refclk;
+
+ switch (freq) {
+ case 2822400:
+ case 3072000:
+ refclk = CS530X_REFCLK_2P822_3P072;
+ break;
+ case 5644800:
+ case 6144000:
+ refclk = CS530X_REFCLK_5P6448_6P144;
+ break;
+ case 11289600:
+ case 12288000:
+ refclk = CS530X_REFCLK_11P2896_12P288;
+ break;
+ case 22579200:
+ case 24576000:
+ refclk = CS530X_REFCLK_24P5792_24P576;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL refclk %d\n", freq);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_FREQ_MASK, refclk);
+}
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ int ret = 0, fs = params_rate(params), bclk;
+ unsigned int fs_val;
+
+
+ switch (fs) {
+ case 32000:
+ fs_val = CS530X_FS_32K;
+ break;
+ case 44100:
+ case 48000:
+ fs_val = CS530X_FS_48K_44P1K;
+ break;
+ case 88200:
+ case 96000:
+ fs_val = CS530X_FS_96K_88P2K;
+ break;
+ case 176400:
+ case 192000:
+ fs_val = CS530X_FS_192K_176P4K;
+ break;
+ case 356800:
+ case 384000:
+ fs_val = CS530X_FS_384K_356P8K;
+ break;
+ case 705600:
+ case 768000:
+ fs_val = CS530X_FS_768K_705P6K;
+ break;
+ default:
+ dev_err(component->dev, "Invalid sample rate %d\n", fs);
+ return -EINVAL;
+ }
+
+ cs530x->fs = fs;
+ regmap_update_bits(regmap, CS530X_CLK_CFG_1,
+ CS530X_SAMPLE_RATE_MASK, fs_val);
+
+
+ if (regmap_test_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_TDM_EN_MASK)) {
+ dev_dbg(component->dev, "Configuring for %d %d bit TDM slots\n",
+ cs530x->tdm_slots, cs530x->tdm_width);
+ bclk = snd_soc_tdm_params_to_bclk(params,
+ cs530x->tdm_width,
+ cs530x->tdm_slots,
+ 1);
+ } else {
+ bclk = snd_soc_params_to_bclk(params);
+ }
+
+ if (!regmap_test_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK)) {
+ ret = cs530x_set_pll_refclk(component, bclk);
+ if (ret)
+ return ret;
+ }
+
+ return cs530x_set_bclk(component, bclk);
+}
+
+static int cs530x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs530x_priv *priv = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = priv->regmap;
+ unsigned int asp_fmt, asp_cfg = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ asp_cfg = CS530X_ASP_PRIMARY;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ asp_fmt = CS530X_ASP_FMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ asp_fmt = CS530X_ASP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ asp_fmt = CS530X_ASP_FMT_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg |= CS530X_ASP_BCLK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, CS530X_ASP_CFG,
+ CS530X_ASP_PRIMARY | CS530X_ASP_BCLK_INV,
+ asp_cfg);
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_FMT_MASK, asp_fmt);
+}
+
+static bool cs530x_check_mclk_freq(struct snd_soc_component *component,
+ const unsigned int freq)
+{
+ switch (freq) {
+ case 24576000:
+ case 22579200:
+ case 12288000:
+ case 11289600:
+ return true;
+ default:
+ dev_err(component->dev, "Invalid MCLK %d\n", freq);
+ return false;
+ }
+}
+
+static int cs530x_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 cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int val;
+
+ switch (tx_mask) {
+ case CS530X_0_1_TDM_SLOT_MASK:
+ case CS530X_0_3_TDM_SLOT_MASK:
+ case CS530X_0_7_TDM_SLOT_MASK:
+ val = CS530X_0_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_2_3_TDM_SLOT_MASK:
+ val = CS530X_2_3_TDM_SLOT_VAL;
+ break;
+ case CS530X_4_5_TDM_SLOT_MASK:
+ case CS530X_4_7_TDM_SLOT_MASK:
+ val = CS530X_4_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_6_7_TDM_SLOT_MASK:
+ val = CS530X_6_7_TDM_SLOT_VAL;
+ break;
+ case CS530X_8_9_TDM_SLOT_MASK:
+ case CS530X_8_11_TDM_SLOT_MASK:
+ case CS530X_8_15_TDM_SLOT_MASK:
+ val = CS530X_8_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_10_11_TDM_SLOT_MASK:
+ val = CS530X_10_11_TDM_SLOT_VAL;
+ break;
+ case CS530X_12_13_TDM_SLOT_MASK:
+ case CS530X_12_15_TDM_SLOT_MASK:
+ val = CS530X_12_15_TDM_SLOT_VAL;
+ break;
+ case CS530X_14_15_TDM_SLOT_MASK:
+ val = CS530X_14_15_TDM_SLOT_VAL;
+ break;
+ default:
+ dev_err(component->dev, "Invalid TX slot(s) 0x%x\n", tx_mask);
+ return -EINVAL;
+ }
+
+ cs530x->tdm_width = slot_width;
+ cs530x->tdm_slots = slots;
+
+ return regmap_update_bits(regmap, CS530X_SIGNAL_PATH_CFG,
+ CS530X_ASP_TDM_SLOT_MASK,
+ val << CS530X_ASP_TDM_SLOT_SHIFT);
+}
+
+static const struct snd_soc_dai_ops cs530x_dai_ops = {
+ .set_fmt = cs530x_set_fmt,
+ .hw_params = cs530x_hw_params,
+ .set_tdm_slot = cs530x_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_driver cs530x_dai = {
+ .name = "cs530x-dai",
+ .capture = {
+ .stream_name = "AIF Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &cs530x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int cs530x_set_pll(struct snd_soc_component *component, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+ unsigned int sysclk_src;
+ int ret;
+
+ regmap_read(regmap, CS530X_CLK_CFG_0, &sysclk_src);
+
+ /* Check if the source is the PLL */
+ if ((sysclk_src & CS530X_SYSCLK_SRC_MASK) == 0)
+ return 0;
+
+ switch (source) {
+ case CS530X_PLL_SRC_MCLK:
+ if (!cs530x_check_mclk_freq(component, freq_in))
+ return -EINVAL;
+
+ ret = cs530x_set_pll_refclk(component, freq_in);
+ if (ret)
+ return ret;
+
+ break;
+ case CS530X_PLL_SRC_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_PLL_REFCLK_SRC_MASK, source);
+}
+
+static int cs530x_component_probe(struct snd_soc_component *component)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int num_widgets;
+
+ snd_soc_dapm_new_controls(dapm, cs530x_gen_dapm_widgets,
+ ARRAY_SIZE(cs530x_gen_dapm_widgets));
+
+ switch (cs530x->devtype) {
+ case CS5302:
+ cs530x_add_12_adc_widgets(component);
+ break;
+ case CS5304:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_4ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_4ch_controls,
+ num_widgets);
+ break;
+
+ case CS5308:
+ cs530x_add_12_adc_widgets(component);
+ cs530x_add_34_adc_widgets(component);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_5_to_8_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_5_to_8_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_in_sum_8ch_controls);
+ snd_soc_add_component_controls(component,
+ cs530x_in_sum_8ch_controls,
+ num_widgets);
+
+ num_widgets = ARRAY_SIZE(cs530x_adc_ch58_dapm_widgets);
+ snd_soc_dapm_new_controls(dapm, cs530x_adc_ch58_dapm_widgets,
+ num_widgets);
+
+ snd_soc_dapm_add_routes(dapm, adc_ch5_8_routes,
+ ARRAY_SIZE(adc_ch5_8_routes));
+ break;
+ default:
+ dev_err(component->dev, "Invalid device type %d\n",
+ cs530x->devtype);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct cs530x_priv *cs530x = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs530x->regmap;
+
+ switch (source) {
+ case CS530X_SYSCLK_SRC_MCLK:
+ if (freq != 24560000 && freq != 22572000) {
+ dev_err(component->dev, "Invalid MCLK source rate %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ cs530x->mclk_rate = freq;
+ break;
+ case CS530X_SYSCLK_SRC_PLL:
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id %d\n", clk_id);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS530X_CLK_CFG_0,
+ CS530X_SYSCLK_SRC_MASK,
+ source << CS530X_SYSCLK_SRC_SHIFT);
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs530x = {
+ .probe = cs530x_component_probe,
+ .set_sysclk = cs530x_set_sysclk,
+ .set_pll = cs530x_set_pll,
+ .endianness = 1,
+};
+
+const struct regmap_config cs530x_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+
+ .max_register = CS530X_MAX_REGISTER,
+ .readable_reg = cs530x_readable_register,
+ .writeable_reg = cs530x_writeable_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs530x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs530x_reg_defaults),
+};
+EXPORT_SYMBOL_NS_GPL(cs530x_regmap, "SND_SOC_CS530X");
+
+static int cs530x_check_device_id(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ unsigned int dev_id, rev;
+ int ret;
+
+ ret = regmap_read(cs530x->regmap, CS530X_DEVID, &dev_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read device ID\n");
+
+ ret = regmap_read(cs530x->regmap, CS530X_REVID, &rev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Can't read REV ID\n");
+
+ dev_dbg(dev, "Device ID 0x%x Rev ID 0x%x\n", dev_id, rev);
+
+ switch (dev_id) {
+ case CS530X_2CH_ADC_DEV_ID:
+ cs530x->num_adcs = 2;
+ break;
+ case CS530X_4CH_ADC_DEV_ID:
+ cs530x->num_adcs = 4;
+ break;
+ case CS530X_8CH_ADC_DEV_ID:
+ cs530x->num_adcs = 8;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid device ID 0x%x\n",
+ dev_id);
+ }
+
+ return 0;
+}
+
+static int cs530x_parse_device_properties(struct cs530x_priv *cs530x)
+{
+ struct regmap *regmap = cs530x->regmap;
+ struct device *dev = cs530x->dev;
+ unsigned int val = 0;
+
+ switch (cs530x->num_adcs) {
+ case 8:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin78"))
+ val = CS530X_IN78_HIZ;
+
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin56"))
+ val |= CS530X_IN56_HIZ;
+
+ fallthrough;
+ case 4:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin34"))
+ val |= CS530X_IN34_HIZ;
+
+ fallthrough;
+ case 2:
+ if (device_property_read_bool(dev, "cirrus,in-hiz-pin12"))
+ val |= CS530X_IN12_HIZ;
+
+ return regmap_set_bits(regmap, CS530X_IN_HIZ, val);
+ default:
+ return dev_err_probe(dev, -EINVAL,
+ "Invalid number of adcs %d\n",
+ cs530x->num_adcs);
+ }
+}
+
+int cs530x_probe(struct cs530x_priv *cs530x)
+{
+ struct device *dev = cs530x->dev;
+ int ret, i;
+
+ cs530x->dev_dai = devm_kmemdup(dev, &cs530x_dai,
+ sizeof(*(cs530x->dev_dai)),
+ GFP_KERNEL);
+ if (!cs530x->dev_dai)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(cs530x->supplies); i++)
+ cs530x->supplies[i].supply = cs530x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to request supplies");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+ if (ret != 0)
+ return dev_err_probe(dev, ret, "Failed to enable supplies");
+
+ cs530x->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(cs530x->reset_gpio)) {
+ ret = dev_err_probe(dev, PTR_ERR(cs530x->reset_gpio),
+ "Reset gpio not available\n");
+ goto err_regulator;
+ }
+
+ if (cs530x->reset_gpio) {
+ usleep_range(2000, 2100);
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 0);
+ }
+
+ usleep_range(5000, 5100);
+ ret = cs530x_check_device_id(cs530x);
+ if (ret)
+ goto err_reset;
+
+ if (!cs530x->reset_gpio) {
+ ret = regmap_write(cs530x->regmap, CS530X_SW_RESET,
+ CS530X_SW_RST_VAL);
+ if (ret) {
+ dev_err_probe(dev, ret, "Soft Reset Failed\n");
+ goto err_reset;
+ }
+ }
+
+ ret = cs530x_parse_device_properties(cs530x);
+ if (ret)
+ goto err_reset;
+
+ cs530x->dev_dai->capture.channels_max = cs530x->num_adcs;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_dev_cs530x, cs530x->dev_dai, 1);
+ if (ret) {
+ dev_err_probe(dev, ret, "Can't register cs530x component\n");
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ gpiod_set_value_cansleep(cs530x->reset_gpio, 1);
+
+err_regulator:
+ regulator_bulk_disable(ARRAY_SIZE(cs530x->supplies),
+ cs530x->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs530x_probe, "SND_SOC_CS530X");
+
+MODULE_DESCRIPTION("CS530X CODEC Driver");
+MODULE_AUTHOR("Paul Handrigan <paulha@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h
new file mode 100644
index 000000000000..f473e33eb835
--- /dev/null
+++ b/sound/soc/codecs/cs530x.h
@@ -0,0 +1,223 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS530x CODEC driver internal data
+ *
+ * Copyright (C) 2023-2024 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef _CS530X_H
+#define _CS530X_H
+
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+/* Devices */
+#define CS530X_2CH_ADC_DEV_ID 0x5302
+#define CS530X_4CH_ADC_DEV_ID 0x5304
+#define CS530X_8CH_ADC_DEV_ID 0x5308
+
+/* Registers */
+
+#define CS530X_DEVID 0x0000000
+#define CS530X_REVID 0x0000004
+#define CS530X_SW_RESET 0x0000022
+
+#define CS530X_CLK_CFG_0 0x0000040
+#define CS530X_CLK_CFG_1 0x0000042
+#define CS530X_CHIP_ENABLE 0x0000044
+#define CS530X_ASP_CFG 0x0000048
+#define CS530X_SIGNAL_PATH_CFG 0x0000050
+#define CS530X_IN_ENABLES 0x0000080
+#define CS530X_IN_RAMP_SUM 0x0000082
+#define CS530X_IN_FILTER 0x0000086
+#define CS530X_IN_HIZ 0x0000088
+#define CS530X_IN_INV 0x000008A
+#define CS530X_IN_VOL_CTRL1_0 0x0000090
+#define CS530X_IN_VOL_CTRL1_1 0x0000092
+#define CS530X_IN_VOL_CTRL2_0 0x0000094
+#define CS530X_IN_VOL_CTRL2_1 0x0000096
+#define CS530X_IN_VOL_CTRL3_0 0x0000098
+#define CS530X_IN_VOL_CTRL3_1 0x000009A
+#define CS530X_IN_VOL_CTRL4_0 0x000009C
+#define CS530X_IN_VOL_CTRL4_1 0x000009E
+#define CS530X_IN_VOL_CTRL5 0x00000A0
+
+#define CS530X_PAD_FN 0x0003D24
+#define CS530X_PAD_LVL 0x0003D28
+
+#define CS530X_MAX_REGISTER CS530X_PAD_LVL
+
+/* Register Fields */
+
+/* REVID */
+#define CS530X_MTLREVID GENMASK(3, 0)
+#define CS530X_AREVID GENMASK(7, 4)
+
+/* SW_RESET */
+#define CS530X_SW_RST_SHIFT 8
+#define CS530X_SW_RST_VAL (0x5A << CS530X_SW_RST_SHIFT)
+
+/* CLK_CFG_0 */
+#define CS530X_PLL_REFCLK_SRC_MASK BIT(0)
+#define CS530X_PLL_REFCLK_FREQ_MASK GENMASK(5, 4)
+#define CS530X_SYSCLK_SRC_MASK BIT(12)
+#define CS530X_SYSCLK_SRC_SHIFT 12
+#define CS530X_REFCLK_2P822_3P072 0
+#define CS530X_REFCLK_5P6448_6P144 0x10
+#define CS530X_REFCLK_11P2896_12P288 0x20
+#define CS530X_REFCLK_24P5792_24P576 0x30
+
+/* CLK_CFG_1 */
+#define CS530X_SAMPLE_RATE_MASK GENMASK(2, 0)
+#define CS530X_FS_32K 0
+#define CS530X_FS_48K_44P1K 1
+#define CS530X_FS_96K_88P2K 2
+#define CS530X_FS_192K_176P4K 3
+#define CS530X_FS_384K_356P8K 4
+#define CS530X_FS_768K_705P6K 5
+
+/* CHIP_ENABLE */
+#define CS530X_GLOBAL_EN BIT(0)
+
+/* ASP_CFG */
+#define CS530X_ASP_BCLK_FREQ_MASK GENMASK(1, 0)
+#define CS530X_ASP_PRIMARY BIT(5)
+#define CS530X_ASP_BCLK_INV BIT(6)
+#define CS530X_BCLK_2P822_3P072 0
+#define CS530X_BCLK_5P6448_6P144 1
+#define CS530X_BCLK_11P2896_12P288 2
+#define CS530X_BCLK_24P5792_24P576 3
+
+/* SIGNAL_PATH_CFG */
+#define CS530X_ASP_FMT_MASK GENMASK(2, 0)
+#define CS530X_ASP_TDM_SLOT_MASK GENMASK(5, 3)
+#define CS530X_ASP_TDM_SLOT_SHIFT 3
+#define CS530X_ASP_CH_REVERSE BIT(9)
+#define CS530X_TDM_EN_MASK BIT(2)
+#define CS530X_ASP_FMT_I2S 0
+#define CS530X_ASP_FMT_LJ 1
+#define CS530X_ASP_FMT_DSP_A 0x6
+
+/* TDM Slots */
+#define CS530X_0_1_TDM_SLOT_MASK GENMASK(1, 0)
+#define CS530X_0_3_TDM_SLOT_MASK GENMASK(3, 0)
+#define CS530X_0_7_TDM_SLOT_MASK GENMASK(7, 0)
+#define CS530X_0_7_TDM_SLOT_VAL 0
+
+#define CS530X_2_3_TDM_SLOT_MASK GENMASK(3, 2)
+#define CS530X_2_3_TDM_SLOT_VAL 1
+
+#define CS530X_4_5_TDM_SLOT_MASK GENMASK(5, 4)
+#define CS530X_4_7_TDM_SLOT_MASK GENMASK(7, 4)
+#define CS530X_4_7_TDM_SLOT_VAL 2
+
+#define CS530X_6_7_TDM_SLOT_MASK GENMASK(7, 6)
+#define CS530X_6_7_TDM_SLOT_VAL 3
+
+#define CS530X_8_9_TDM_SLOT_MASK GENMASK(9, 8)
+#define CS530X_8_11_TDM_SLOT_MASK GENMASK(11, 8)
+#define CS530X_8_15_TDM_SLOT_MASK GENMASK(15, 8)
+#define CS530X_8_15_TDM_SLOT_VAL 4
+
+#define CS530X_10_11_TDM_SLOT_MASK GENMASK(11, 10)
+#define CS530X_10_11_TDM_SLOT_VAL 5
+
+#define CS530X_12_13_TDM_SLOT_MASK GENMASK(13, 12)
+#define CS530X_12_15_TDM_SLOT_MASK GENMASK(15, 12)
+#define CS530X_12_15_TDM_SLOT_VAL 6
+
+#define CS530X_14_15_TDM_SLOT_MASK GENMASK(15, 14)
+#define CS530X_14_15_TDM_SLOT_VAL 7
+
+/* IN_RAMP_SUM */
+#define CS530X_RAMP_RATE_INC_SHIFT 0
+#define CS530X_RAMP_RATE_DEC_SHIFT 4
+#define CS530X_IN_SUM_MODE_SHIFT 13
+
+/* IN_FILTER */
+#define CS530X_IN_FILTER_SHIFT 8
+#define CS530X_IN_HPF_EN_SHIFT 12
+
+/* IN_HIZ */
+#define CS530X_IN12_HIZ BIT(0)
+#define CS530X_IN34_HIZ BIT(1)
+#define CS530X_IN56_HIZ BIT(2)
+#define CS530X_IN78_HIZ BIT(3)
+
+/* IN_INV */
+#define CS530X_IN1_INV_SHIFT 0
+#define CS530X_IN2_INV_SHIFT 1
+#define CS530X_IN3_INV_SHIFT 2
+#define CS530X_IN4_INV_SHIFT 3
+#define CS530X_IN5_INV_SHIFT 4
+#define CS530X_IN6_INV_SHIFT 5
+#define CS530X_IN7_INV_SHIFT 6
+#define CS530X_IN8_INV_SHIFT 7
+
+/* IN_VOL_CTLy_z */
+#define CS530X_IN_MUTE BIT(15)
+
+/* IN_VOL_CTL5 */
+#define CS530X_IN_VU BIT(0)
+
+/* PAD_FN */
+#define CS530X_DOUT2_FN BIT(0)
+#define CS530X_DOUT3_FN BIT(1)
+#define CS530X_DOUT4_FN BIT(2)
+#define CS530X_SPI_CS_FN BIT(3)
+#define CS530X_CONFIG2_FN BIT(6)
+#define CS530X_CONFIG3_FN BIT(7)
+#define CS530X_CONFIG4_FN BIT(8)
+#define CS530X_CONFIG5_FN BIT(9)
+
+/* PAD_LVL */
+#define CS530X_CONFIG2_LVL BIT(6)
+#define CS530X_CONFIG3_LVL BIT(7)
+#define CS530X_CONFIG4_LVL BIT(8)
+#define CS530X_CONFIG5_LVL BIT(9)
+
+/* System Clock Source */
+#define CS530X_SYSCLK_SRC_MCLK 0
+#define CS530X_SYSCLK_SRC_PLL 1
+
+/* PLL Reference Clock Source */
+#define CS530X_PLL_SRC_BCLK 0
+#define CS530X_PLL_SRC_MCLK 1
+
+#define CS530X_NUM_SUPPLIES 2
+
+enum cs530x_type {
+ CS5302,
+ CS5304,
+ CS5308,
+};
+
+/* codec private data */
+struct cs530x_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct snd_soc_dai_driver *dev_dai;
+
+ enum cs530x_type devtype;
+ int num_adcs;
+ int num_dacs;
+
+ struct regulator_bulk_data supplies[CS530X_NUM_SUPPLIES];
+
+ unsigned int mclk_rate;
+
+ int tdm_width;
+ int tdm_slots;
+ int fs;
+ int adc_pairs_count;
+
+ struct gpio_desc *reset_gpio;
+};
+
+extern const struct regmap_config cs530x_regmap;
+int cs530x_probe(struct cs530x_priv *cs530x);
+
+#endif
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 000000000000..61bf72681674
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,1106 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * cs53l30.c -- CS53l30 ALSA Soc Audio driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Authors: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ aspcfg |= CS53L30_ASP_MS;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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, &reg);
+ 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, &reg);
+ for (i = 0; i < inter_max_check; i++) {
+ if (inter_max_check < 10) {
+ usleep_range(1000, 1100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ if (reg & CS53L30_PDN_DONE)
+ break;
+ } else {
+ usleep_range(10000, 10100);
+ regmap_read(priv->regmap, CS53L30_IS, &reg);
+ 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);
+}
+
+/*
+ * 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;
+}
+
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000)
+
+#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 = {
+ .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 const 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, &reg);
+ 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);
+}
+
+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;
+}
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ 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" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ .of_match_table = cs53l30_of_match,
+ .pm = pm_ptr(&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, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs53l30.h b/sound/soc/codecs/cs53l30.h
new file mode 100644
index 000000000000..071547c55719
--- /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 <Paul.Handrigan@cirrus.com>,
+ * Tim Howe <Tim.Howe@cirrus.com>
+ */
+
+#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
index 0b10979513c4..9d54141a0cd1 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cx20442.c -- CX20442 ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
* Initially based on sound/soc/codecs/wm8400.c
* Copyright 2008, 2009 Wolfson Microelectronics PLC.
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/tty.h>
@@ -26,8 +22,9 @@
struct cx20442_priv {
- void *control_data;
+ struct tty_struct *tty;
struct regulator *por;
+ u8 reg_cache;
};
#define CX20442_PM 0x0
@@ -88,15 +85,15 @@ static const struct snd_soc_dapm_route cx20442_audio_map[] = {
{"ADC", NULL, "Input Mixer"},
};
-static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static unsigned int cx20442_read_reg_cache(struct snd_soc_component *component,
+ unsigned int reg)
{
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- return reg_cache[reg];
+ return cx20442->reg_cache;
}
enum v253_vls {
@@ -152,24 +149,23 @@ static int cx20442_pm_to_v253_vsp(u8 value)
return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
}
-static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+static int cx20442_write(struct snd_soc_component *component, unsigned int reg,
unsigned int value)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
- u8 *reg_cache = codec->reg_cache;
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int vls, vsp, old, len;
char buf[18];
- if (reg >= codec->driver->reg_cache_size)
+ if (reg >= 1)
return -EINVAL;
- /* hw_write and control_data pointers required for talking to the modem
+ /* tty and write pointers required for talking to the modem
* are expected to be set by the line discipline initialization code */
- if (!codec->hw_write || !cx20442->control_data)
+ if (!cx20442->tty || !cx20442->tty->ops->write)
return -EIO;
- old = reg_cache[reg];
- reg_cache[reg] = value;
+ old = cx20442->reg_cache;
+ cx20442->reg_cache = value;
vls = cx20442_pm_to_v253_vls(value);
if (vls < 0)
@@ -193,14 +189,13 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
return -ENOMEM;
- dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
- if (codec->hw_write(cx20442->control_data, buf, len) != len)
+ 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
*
@@ -211,7 +206,7 @@ static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
*/
/* Modem init: echo off, digital speaker off, quiet off, voice mode */
-static const char *v253_init = "ate0m0q0+fclass=8\r";
+static const char v253_init[] = "ate0m0q0+fclass=8\r";
/* Line discipline .open() */
static int v253_open(struct tty_struct *tty)
@@ -226,6 +221,7 @@ static int v253_open(struct tty_struct *tty)
if (!tty->disc_data)
return -ENODEV;
+ tty->receive_room = 16;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
@@ -240,65 +236,55 @@ err:
/* Line discipline .close() */
static void v253_close(struct tty_struct *tty)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
tty->disc_data = NULL;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
/* Prevent the codec driver from further accessing the modem */
- codec->hw_write = NULL;
- cx20442->control_data = NULL;
- codec->component.card->pop_time = 0;
+ cx20442->tty = NULL;
+ component->card->pop_time = 0;
}
/* Line discipline .hangup() */
-static int v253_hangup(struct tty_struct *tty)
+static void v253_hangup(struct tty_struct *tty)
{
v253_close(tty);
- return 0;
}
/* Line discipline .receive_buf() */
-static void v253_receive(struct tty_struct *tty,
- const unsigned char *cp, char *fp, int count)
+static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp,
+ size_t count)
{
- struct snd_soc_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = tty->disc_data;
struct cx20442_priv *cx20442;
- if (!codec)
+ if (!component)
return;
- cx20442 = snd_soc_codec_get_drvdata(codec);
+ cx20442 = snd_soc_component_get_drvdata(component);
- if (!cx20442->control_data) {
+ if (!cx20442->tty) {
/* First modem response, complete setup procedure */
/* Set up codec driver access to modem controls */
- cx20442->control_data = tty;
- codec->hw_write = (hw_write_t)tty->ops->write;
- codec->component.card->pop_time = 1;
+ cx20442->tty = tty;
+ component->card->pop_time = 1;
}
}
-/* Line discipline .write_wakeup() */
-static void v253_wakeup(struct tty_struct *tty)
-{
-}
-
struct tty_ldisc_ops v253_ops = {
- .magic = TTY_LDISC_MAGIC,
.name = "cx20442",
.owner = THIS_MODULE,
.open = v253_open,
.close = v253_close,
.hangup = v253_hangup,
.receive_buf = v253_receive,
- .write_wakeup = v253_wakeup,
};
EXPORT_SYMBOL_GPL(v253_ops);
@@ -325,15 +311,15 @@ static struct snd_soc_dai_driver cx20442_dai = {
},
};
-static int cx20442_set_bias_level(struct snd_soc_codec *codec,
+static int cx20442_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
int err = 0;
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level != SND_SOC_BIAS_STANDBY)
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_STANDBY)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -341,7 +327,7 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
err = regulator_enable(cx20442->por);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level != SND_SOC_BIAS_PREPARE)
+ if (snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_PREPARE)
break;
if (IS_ERR(cx20442->por))
err = PTR_ERR(cx20442->por);
@@ -351,13 +337,11 @@ static int cx20442_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- if (!err)
- codec->dapm.bias_level = level;
return err;
}
-static int cx20442_codec_probe(struct snd_soc_codec *codec)
+static int cx20442_component_probe(struct snd_soc_component *component)
{
struct cx20442_priv *cx20442;
@@ -365,25 +349,43 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
if (cx20442 == NULL)
return -ENOMEM;
- cx20442->por = regulator_get(codec->dev, "POR");
- if (IS_ERR(cx20442->por))
- dev_warn(codec->dev, "failed to get the regulator");
- cx20442->control_data = NULL;
+ 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;
+ }
- snd_soc_codec_set_drvdata(codec, cx20442);
- codec->hw_write = NULL;
- codec->component.card->pop_time = 0;
+ cx20442->tty = NULL;
+
+ snd_soc_component_set_drvdata(component, cx20442);
+ component->card->pop_time = 0;
return 0;
}
/* power down chip */
-static int cx20442_codec_remove(struct snd_soc_codec *codec)
+static void cx20442_component_remove(struct snd_soc_component *component)
{
- struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
+ struct cx20442_priv *cx20442 = snd_soc_component_get_drvdata(component);
- if (cx20442->control_data) {
- struct tty_struct *tty = cx20442->control_data;
+ if (cx20442->tty) {
+ struct tty_struct *tty = cx20442->tty;
tty_hangup(tty);
}
@@ -392,38 +394,29 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec)
regulator_put(cx20442->por);
}
- snd_soc_codec_set_drvdata(codec, NULL);
+ snd_soc_component_set_drvdata(component, NULL);
kfree(cx20442);
- return 0;
}
-static const u8 cx20442_reg;
-
-static struct snd_soc_codec_driver cx20442_codec_dev = {
- .probe = cx20442_codec_probe,
- .remove = cx20442_codec_remove,
- .set_bias_level = cx20442_set_bias_level,
- .reg_cache_default = &cx20442_reg,
- .reg_cache_size = 1,
- .reg_word_size = sizeof(u8),
- .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),
+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 snd_soc_register_codec(&pdev->dev,
- &cx20442_codec_dev, &cx20442_dai, 1);
-}
-
-static int __exit cx20442_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ return devm_snd_soc_register_component(&pdev->dev,
+ &cx20442_component_dev, &cx20442_dai, 1);
}
static struct platform_driver cx20442_platform_driver = {
@@ -431,7 +424,6 @@ static struct platform_driver cx20442_platform_driver = {
.name = "cx20442-codec",
},
.probe = cx20442_platform_probe,
- .remove = __exit_p(cx20442_platform_remove),
};
module_platform_driver(cx20442_platform_driver);
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
index c7a7c79ef0cd..bb897bcb2486 100644
--- a/sound/soc/codecs/cx20442.h
+++ b/sound/soc/codecs/cx20442.h
@@ -1,13 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* cx20442.h -- audio driver for CX20442
*
* Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _CX20442_CODEC_H
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
new file mode 100644
index 000000000000..934526f8f292
--- /dev/null
+++ b/sound/soc/codecs/cx2072x.c
@@ -0,0 +1,1718 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC CX20721/CX20723 codec driver
+//
+// Copyright: (C) 2017 Conexant Systems, Inc.
+// Author: Simon Ho, <Simon.ho@conexant.com>
+//
+// TODO: add support for TDM mode.
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#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);
+
+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 cx2072x_runtime_suspend(struct device *dev)
+{
+ struct cx2072x_priv *cx2072x = dev_get_drvdata(dev);
+
+ clk_disable_unprepare(cx2072x->mclk);
+ return 0;
+}
+
+static int 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" },
+ { "cx20723" },
+ {}
+};
+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 = {
+ RUNTIME_PM_OPS(cx2072x_runtime_suspend, cx2072x_runtime_resume, NULL)
+ 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 = pm_ptr(&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 <simon.ho@conexant.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cx2072x.h b/sound/soc/codecs/cx2072x.h
new file mode 100644
index 000000000000..09e3a92b184f
--- /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, <Simon.ho@conexant.com>
+ */
+
+#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
index 21810e5f3321..a889f05119f8 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1,19 +1,14 @@
-/*
- * DA7210 ALSA Soc codec driver
- *
- * Copyright (c) 2009 Dialog Semiconductor
- * Written by David Chen <Dajun.chen@diasemi.com>
- *
- * Copyright (C) 2009 Renesas Solutions Corp.
- * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
- *
- * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- */
+// SPDX-License-Identifier: GPL-2.0+
+//
+// DA7210 ALSA Soc codec driver
+//
+// Copyright (c) 2009 Dialog Semiconductor
+// Written by David Chen <Dajun.chen@diasemi.com>
+//
+// Copyright (C) 2009 Renesas Solutions Corp.
+// Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+//
+// Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -267,33 +262,29 @@ enum clk_src {
*
* Reserved area are considered as "mute".
*/
-static const unsigned int hp_out_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 0x11, 0x3f, TLV_DB_SCALE_ITEM(-5400, 150, 0)
+);
-static const unsigned int lineout_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int mono_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int aux1_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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);
@@ -335,12 +326,12 @@ static SOC_ENUM_SINGLE_DECL(da7210_hp_mode_sel,
static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, DA7210_CONTROL) & DA7210_NOISE_SUP_EN) {
- dev_dbg(codec->dev,
+ 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;
}
@@ -358,32 +349,32 @@ static int da7210_put_alc_sw(struct snd_kcontrol *kcontrol,
static int da7210_put_noise_sup_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, DA7210_ADC) & DA7210_ADC_ALC_EN)
+ if (snd_soc_component_read(component, DA7210_ADC) & DA7210_ADC_ALC_EN)
goto err;
/* Check ZC for HP and AUX1 PGA */
- if ((snd_soc_read(codec, DA7210_ZERO_CROSS) &
+ 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_read(codec, DA7210_IN_GAIN);
+ 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_read(codec, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
+ if (((snd_soc_component_read(component, DA7210_AUX1_L) & DA7210_AUX1_L_VOL) <
DA7210_AUX1_MIN_VOL_NS) ||
- ((snd_soc_read(codec, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
+ ((snd_soc_component_read(component, DA7210_AUX1_R) & DA7210_AUX1_R_VOL) <
DA7210_AUX1_MIN_VOL_NS))
goto err;
}
@@ -680,7 +671,7 @@ struct da7210_priv {
int master;
};
-static struct reg_default da7210_reg_defaults[] = {
+static const struct reg_default da7210_reg_defaults[] = {
{ 0x00, 0x00 },
{ 0x01, 0x11 },
{ 0x03, 0x00 },
@@ -764,19 +755,19 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, DA7210_DAI_SRC_SEL,
+ snd_soc_component_write(component, DA7210_DAI_SRC_SEL,
DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
/* Enable DAI */
- snd_soc_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
+ snd_soc_component_write(component, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
- dai_cfg1 = 0xFC & snd_soc_read(codec, DA7210_DAI_CFG1);
+ dai_cfg1 = 0xFC & snd_soc_component_read(component, DA7210_DAI_CFG1);
switch (params_width(params)) {
case 16:
@@ -795,7 +786,7 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+ snd_soc_component_write(component, DA7210_DAI_CFG1, dai_cfg1);
switch (params_rate(params)) {
case 8000:
@@ -843,17 +834,17 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_PLL_FS_MASK, fs);
+ 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_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP, 0);
if (!da7210->master) {
/* PLL slave mode, also enable SRM */
- snd_soc_update_bits(codec, DA7210_PLL,
+ snd_soc_component_update_bits(component, DA7210_PLL,
(DA7210_MCLK_SRM_EN |
DA7210_MCLK_DET_EN),
(DA7210_MCLK_SRM_EN |
@@ -861,13 +852,13 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
}
} else {
/* PLL bypass mode, enable PLL bypass and Auto Detection */
- snd_soc_update_bits(codec, DA7210_PLL, DA7210_MCLK_DET_EN,
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_MCLK_DET_EN,
DA7210_MCLK_DET_EN);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3, DA7210_PLL_BYP,
+ snd_soc_component_update_bits(component, DA7210_PLL_DIV3, DA7210_PLL_BYP,
DA7210_PLL_BYP);
}
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1,
DA7210_SC_MST_EN, DA7210_SC_MST_EN);
return 0;
@@ -878,24 +869,24 @@ static int da7210_hw_params(struct snd_pcm_substream *substream,
*/
static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, DA7210_DAI_CFG1);
- dai_cfg3 = 0xfc & snd_soc_read(codec, DA7210_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_read(codec, DA7210_PLL) & DA7210_PLL_EN) &&
- (!(snd_soc_read(codec, DA7210_PLL_DIV3) & DA7210_PLL_BYP)))
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
da7210->master = 1;
dai_cfg1 |= DA7210_DAI_MODE_MASTER;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
da7210->master = 0;
dai_cfg1 |= DA7210_DAI_MODE_SLAVE;
break;
@@ -927,21 +918,21 @@ static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
*/
dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
- snd_soc_write(codec, DA7210_DAI_CFG1, dai_cfg1);
- snd_soc_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+ 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)
+static int da7210_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- u8 mute_reg = snd_soc_read(codec, DA7210_DAC_HPF) & 0xFB;
+ struct snd_soc_component *component = dai->component;
+ u8 mute_reg = snd_soc_component_read(component, DA7210_DAC_HPF) & 0xFB;
if (mute)
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg | 0x4);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg | 0x4);
else
- snd_soc_write(codec, DA7210_DAC_HPF, mute_reg);
+ snd_soc_component_write(component, DA7210_DAC_HPF, mute_reg);
return 0;
}
@@ -951,8 +942,8 @@ static int da7210_mute(struct snd_soc_dai *dai, int mute)
static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -980,20 +971,22 @@ static int da7210_set_dai_sysclk(struct snd_soc_dai *codec_dai,
/**
* da7210_set_dai_pll :Configure the codec PLL
- * @param codec_dai : pointer to codec DAI
- * @param pll_id : da7210 has only one pll, so pll_id is always zero
- * @param fref : MCLK frequency, should be < 20MHz
- * @param fout : FsDM value, Refer page 44 & 45 of datasheet
- * @return int : Zero for success, negative error code for error
+ * @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_codec *codec = codec_dai->codec;
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1018,18 +1011,18 @@ static int da7210_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto err;
/* Disable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN, 0);
/* Write PLL dividers */
- snd_soc_write(codec, DA7210_PLL_DIV1, pll_div1);
- snd_soc_write(codec, DA7210_PLL_DIV2, pll_div2);
- snd_soc_update_bits(codec, DA7210_PLL_DIV3,
+ 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_update_bits(codec, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
+ snd_soc_component_update_bits(component, DA7210_PLL, DA7210_PLL_EN, DA7210_PLL_EN);
/* Enable active mode */
- snd_soc_update_bits(codec, DA7210_STARTUP1, DA7210_SC_MST_EN,
+ snd_soc_component_update_bits(component, DA7210_STARTUP1, DA7210_SC_MST_EN,
DA7210_SC_MST_EN);
return 0;
err:
@@ -1043,7 +1036,8 @@ static const struct snd_soc_dai_ops da7210_dai_ops = {
.set_fmt = da7210_set_dai_fmt,
.set_sysclk = da7210_set_dai_sysclk,
.set_pll = da7210_set_dai_pll,
- .digital_mute = da7210_mute,
+ .mute_stream = da7210_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da7210_dai = {
@@ -1065,56 +1059,56 @@ static struct snd_soc_dai_driver da7210_dai = {
.formats = DA7210_FORMATS,
},
.ops = &da7210_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7210_probe(struct snd_soc_codec *codec)
+static int da7210_probe(struct snd_soc_component *component)
{
- struct da7210_priv *da7210 = snd_soc_codec_get_drvdata(codec);
+ struct da7210_priv *da7210 = snd_soc_component_get_drvdata(component);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ 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_write(codec, DA7210_CONTROL, DA7210_REG_EN | DA7210_BIAS_EN);
+ 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_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
- snd_soc_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
+ 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_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
- snd_soc_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
+ 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_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+ snd_soc_component_write(component, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
/*
* DAC settings
*/
/* Enable Left and Right DAC */
- snd_soc_write(codec, DA7210_DAC_SEL,
+ 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_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
- snd_soc_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
+ 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_write(codec, DA7210_HP_CFG,
+ 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_write(codec, DA7210_SOFTMUTE, DA7210_RAMP_EN);
+ snd_soc_component_write(component, DA7210_SOFTMUTE, DA7210_RAMP_EN);
/*
* For DA7210 codec, there are two ways to enable/disable analog IOs
@@ -1142,47 +1136,48 @@ static int da7210_probe(struct snd_soc_codec *codec)
*/
/* Enable Line out amplifiers */
- snd_soc_write(codec, DA7210_OUT1_L, DA7210_OUT1_L_EN);
- snd_soc_write(codec, DA7210_OUT1_R, DA7210_OUT1_R_EN);
- snd_soc_write(codec, DA7210_OUT2, DA7210_OUT2_EN |
+ 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_write(codec, DA7210_AUX1_L, DA7210_AUX1_L_EN);
- snd_soc_write(codec, DA7210_AUX1_R, DA7210_AUX1_R_EN);
+ 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_write(codec, DA7210_AUX2, DA7210_AUX2_EN);
+ snd_soc_component_write(component, DA7210_AUX2, DA7210_AUX2_EN);
/* Set PLL Master clock range 10-20 MHz, enable PLL bypass */
- snd_soc_write(codec, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
+ snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
DA7210_PLL_BYP);
/* Diable PLL and bypass it */
- snd_soc_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+ snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000);
/* Activate all enabled subsystem */
- snd_soc_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+ snd_soc_component_write(component, DA7210_STARTUP1, DA7210_SC_MST_EN);
- dev_info(codec->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+ dev_info(component->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
+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 struct reg_default da7210_regmap_i2c_patch[] = {
+static const struct reg_sequence da7210_regmap_i2c_patch[] = {
/* System controller master disable */
{ DA7210_STARTUP1, 0x00 },
@@ -1210,8 +1205,7 @@ static const struct regmap_config da7210_regmap_config_i2c = {
.cache_type = REGCACHE_RBTREE,
};
-static int da7210_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da7210_i2c_probe(struct i2c_client *i2c)
{
struct da7210_priv *da7210;
int ret;
@@ -1235,22 +1229,16 @@ static int da7210_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int da7210_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id da7210_i2c_id[] = {
- { "da7210", 0 },
+ { "da7210" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
@@ -1259,17 +1247,15 @@ MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
static struct i2c_driver da7210_i2c_driver = {
.driver = {
.name = "da7210",
- .owner = THIS_MODULE,
},
.probe = da7210_i2c_probe,
- .remove = da7210_i2c_remove,
.id_table = da7210_i2c_id,
};
#endif
#if defined(CONFIG_SPI_MASTER)
-static struct reg_default da7210_regmap_spi_patch[] = {
+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 },
@@ -1329,25 +1315,17 @@ static int da7210_spi_probe(struct spi_device *spi)
if (ret != 0)
dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
- ret = snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_da7210, &da7210_dai, 1);
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &soc_component_dev_da7210, &da7210_dai, 1);
return ret;
}
-static int da7210_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver da7210_spi_driver = {
.driver = {
.name = "da7210",
- .owner = THIS_MODULE,
},
.probe = da7210_spi_probe,
- .remove = da7210_spi_remove
};
#endif
@@ -1356,6 +1334,8 @@ 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);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 9ec577f0edb4..ae89260ca215 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA7213 ALSA SoC Codec Driver
*
@@ -5,13 +6,12 @@
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
* Based on DA9055 ALSA SoC codec driver.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
+#include <linux/acpi.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
@@ -19,6 +19,8 @@
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <linux/units.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
@@ -28,27 +30,24 @@
/* Gain and Volume */
-static const unsigned int aux_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int digital_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int alc_analog_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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);
@@ -57,6 +56,7 @@ 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);
+static const DECLARE_TLV_DB_SCALE(da7213_tonegen_gain_tlv, -4500, 300, 0);
/* ADC and DAC voice mode (8kHz) high pass cutoff value */
static const char * const da7213_voice_hpf_corner_txt[] = {
@@ -88,6 +88,23 @@ static SOC_ENUM_SINGLE_DECL(da7213_adc_audio_hpf_corner,
DA7213_AUDIO_HPF_CORNER_SHIFT,
da7213_audio_hpf_corner_txt);
+static const char * const da7213_tonegen_dtmf_key_txt[] = {
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+ "*", "#"
+};
+
+static const struct soc_enum da7213_tonegen_dtmf_key =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG1, DA7213_DTMF_REG_SHIFT,
+ DA7213_DTMF_REG_MAX, da7213_tonegen_dtmf_key_txt);
+
+static const char * const da7213_tonegen_swg_sel_txt[] = {
+ "Sum", "SWG1", "SWG2", "Sum"
+};
+
+static const struct soc_enum da7213_tonegen_swg_sel =
+ SOC_ENUM_SINGLE(DA7213_TONE_GEN_CFG2, DA7213_SWG_SEL_SHIFT,
+ DA7213_SWG_SEL_MAX, da7213_tonegen_swg_sel_txt);
+
/* Gain ramping rate value */
static const char * const da7213_gain_ramp_rate_txt[] = {
"nominal rate * 8", "nominal rate * 16", "nominal rate / 16",
@@ -193,7 +210,65 @@ static SOC_ENUM_SINGLE_DECL(da7213_alc_integ_release_rate,
* Control Functions
*/
-static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
+/* Locked Kcontrol calls */
+static int da7213_volsw_locked_get(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_volsw_locked_put(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_get(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+static int da7213_enum_locked_put(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;
+
+ mutex_lock(&da7213->ctrl_lock);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ mutex_unlock(&da7213->ctrl_lock);
+
+ return ret;
+}
+
+/* ALC */
+static int da7213_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -202,17 +277,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA7213_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL, reg_val);
+ 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_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ 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_write(codec, DA7213_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA7213_ALC_CIC_OP_LVL_CTRL,
reg_val | DA7213_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA7213_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA7213_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -220,17 +295,17 @@ static int da7213_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
return sum / DA7213_ALC_AVG_ITERATIONS;
}
-static void da7213_alc_calib_man(struct snd_soc_codec *codec)
+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(codec,
+ avg_left_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da7213_get_alc_data(codec,
+ avg_right_data = da7213_get_alc_data(component,
DA7213_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -238,122 +313,122 @@ static void da7213_alc_calib_man(struct snd_soc_codec *codec)
offset_r = -avg_right_data;
reg_val = (offset_l & DA7213_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA7213_ALC_OFFSET_MAN_M_L, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_U_L, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_M_R, reg_val);
+ 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_write(codec, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
+ snd_soc_component_write(component, DA7213_ALC_OFFSET_MAN_U_R, reg_val);
/* Enable analog/digital gain mode & offset cancellation */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_codec *codec)
+static void da7213_alc_calib_auto(struct snd_soc_component *component)
{
u8 alc_ctrl1;
/* Begin auto calibration and wait for completion */
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1, DA7213_ALC_AUTO_CALIB_EN,
DA7213_ALC_AUTO_CALIB_EN);
do {
- alc_ctrl1 = snd_soc_read(codec, DA7213_ALC_CTRL1);
+ 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(codec->dev,
+ dev_warn(component->dev,
"ALC auto calibration failed with overflow\n");
- snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_update_bits(codec, DA7213_ALC_CTRL1,
+ 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_codec *codec)
+static void da7213_alc_calib(struct snd_soc_component *component)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, DA7213_ADC_L_CTRL);
- adc_r_ctrl = snd_soc_read(codec, DA7213_ADC_R_CTRL);
+ 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_read(codec, DA7213_MIXIN_L_SELECT);
- mixin_r_sel = snd_soc_read(codec, DA7213_MIXIN_R_SELECT);
+ 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_read(codec, DA7213_MIC_1_CTRL);
- mic_2_ctrl = snd_soc_read(codec, DA7213_MIC_2_CTRL);
+ 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_update_bits(codec, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL, DA7213_ADC_EN,
DA7213_ADC_EN);
/* Enable MIC paths */
- snd_soc_update_bits(codec, DA7213_MIXIN_L_SELECT,
+ 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_update_bits(codec, DA7213_MIXIN_R_SELECT,
+ 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_update_bits(codec, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
+ snd_soc_component_update_bits(component, DA7213_MIC_1_CTRL, DA7213_MUTE_EN,
DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_MIC_2_CTRL, 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(codec);
+ da7213_alc_calib_auto(component);
else
- da7213_alc_calib_man(codec);
+ da7213_alc_calib_man(component);
/* Restore MIXIN_L/R_SELECT registers to their original states */
- snd_soc_write(codec, DA7213_MIXIN_L_SELECT, mixin_l_sel);
- snd_soc_write(codec, DA7213_MIXIN_R_SELECT, mixin_r_sel);
+ 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_write(codec, DA7213_ADC_L_CTRL, adc_l_ctrl);
- snd_soc_write(codec, DA7213_ADC_R_CTRL, adc_r_ctrl);
+ 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_write(codec, DA7213_MIC_1_CTRL, mic_1_ctrl);
- snd_soc_write(codec, DA7213_MIC_2_CTRL, mic_2_ctrl);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ da7213_alc_calib(component);
return ret;
}
@@ -361,14 +436,14 @@ static int da7213_put_mixin_gain(struct snd_kcontrol *kcontrol,
static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ da7213_alc_calib(component);
da7213->alc_en = true;
}
} else {
@@ -378,6 +453,64 @@ static int da7213_put_alc_sw(struct snd_kcontrol *kcontrol,
return snd_soc_put_volsw(kcontrol, ucontrol);
}
+/* ToneGen */
+static int da7213_tonegen_freq_get(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);
+ 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(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val, sizeof(val));
+ mutex_unlock(&da7213->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 da7213_tonegen_freq_put(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);
+ 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(&da7213->ctrl_lock);
+ ret = regmap_raw_read(da7213->regmap, reg, &val_old, sizeof(val_old));
+ if (ret == 0 && (val_old != val_new))
+ ret = regmap_raw_write(da7213->regmap, reg,
+ &val_new, sizeof(val_new));
+ mutex_unlock(&da7213->ctrl_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return val_old != val_new;
+}
/*
* KControls
@@ -479,6 +612,37 @@ static const struct snd_kcontrol_new da7213_snd_controls[] = {
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),
+ /* Tone Generator */
+ SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7213_TONE_GEN_CFG2,
+ DA7213_TONE_GEN_GAIN_SHIFT, DA7213_TONE_GEN_GAIN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put, da7213_tonegen_gain_tlv),
+ SOC_ENUM_EXT("ToneGen DTMF Key", da7213_tonegen_dtmf_key,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7213_TONE_GEN_CFG1,
+ DA7213_DTMF_EN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE_EXT("ToneGen Start", DA7213_TONE_GEN_CFG1,
+ DA7213_START_STOPN_SHIFT, DA7213_SWITCH_EN_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7213_tonegen_swg_sel,
+ da7213_enum_locked_get, da7213_enum_locked_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7213_TONE_GEN_FREQ1_L,
+ DA7213_FREQ1_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7213_TONE_GEN_FREQ2_L,
+ DA7213_FREQ2_L_SHIFT, DA7213_FREQ_MAX, DA7213_NO_INVERT,
+ da7213_tonegen_freq_get, da7213_tonegen_freq_put),
+ SOC_SINGLE_EXT("ToneGen On Time", DA7213_TONE_GEN_ON_PER,
+ DA7213_BEEP_ON_PER_SHIFT, DA7213_BEEP_ON_OFF_MAX,
+ DA7213_NO_INVERT, da7213_volsw_locked_get,
+ da7213_volsw_locked_put),
+ SOC_SINGLE("ToneGen Off Time", DA7213_TONE_GEN_OFF_PER,
+ DA7213_BEEP_OFF_PER_SHIFT, DA7213_BEEP_ON_OFF_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,
@@ -728,17 +892,100 @@ static const struct snd_kcontrol_new da7213_dapm_mixoutr_controls[] = {
/*
+ * 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 & DA7213_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, NULL, 0),
+ DA7213_NO_INVERT, da7213_dai_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/*
* Input
@@ -857,6 +1104,9 @@ 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"},
@@ -954,7 +1204,7 @@ static const struct snd_soc_dapm_route da7213_audio_map[] = {
{"LINE", NULL, "Lineout PGA"},
};
-static struct reg_default da7213_reg_defaults[] = {
+static const struct reg_default da7213_reg_defaults[] = {
{ DA7213_DIG_ROUTING_DAI, 0x10 },
{ DA7213_SR, 0x0A },
{ DA7213_REFERENCES, 0x80 },
@@ -1061,9 +1311,9 @@ static bool da7213_volatile_register(struct device *dev, unsigned int reg)
case DA7213_ALC_OFFSET_AUTO_M_R:
case DA7213_ALC_OFFSET_AUTO_U_R:
case DA7213_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1071,14 +1321,33 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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;
@@ -1097,59 +1366,71 @@ static int da7213_hw_params(struct snd_pcm_substream *substream,
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_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_WORD_LENGTH_MASK,
- dai_ctrl);
- snd_soc_write(codec, DA7213_SR, fs);
+ 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_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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:
- dai_clk_mode |= DA7213_DAI_CLK_EN_MASTER_MODE;
+ case SND_SOC_DAIFMT_CBP_CFP:
da7213->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- dai_clk_mode |= DA7213_DAI_CLK_EN_SLAVE_MODE;
+ case SND_SOC_DAIFMT_CBC_CFC:
da7213->master = false;
break;
default:
@@ -1157,17 +1438,46 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* Set clock normal/inverted */
- 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;
+ 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_DAIFMT_IB_IF:
- dai_clk_mode |= DA7213_DAI_WCLK_POL_INV | DA7213_DAI_CLK_POL_INV;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_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;
@@ -1177,40 +1487,56 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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_DAIFMT_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_DAIFMT_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 32 BCLK per WCLK is supported */
- dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_32;
+ /* By default only 64 BCLK per WCLK is supported */
+ dai_clk_mode |= DA7213_DAI_BCLKS_PER_WCLK_64;
- snd_soc_write(codec, DA7213_DAI_CLK_MODE, dai_clk_mode);
- snd_soc_update_bits(codec, DA7213_DAI_CTRL, DA7213_DAI_FORMAT_MASK,
+ 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)
+static int da7213_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, DA7213_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_MUTE_EN, 0);
}
@@ -1220,97 +1546,141 @@ static int da7213_mute(struct snd_soc_dai *dai, int mute)
#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_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
+static int da7213_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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;
+
+ /* Maybe audio stream is closing. */
+ if (freq == 0)
+ return 0;
+
+ if (((freq < da7213->fin_min_rate) && (freq != 32768)) || (freq > 54000000)) {
+ dev_err(component->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- if ((freq == 32768) ||
- ((freq >= 5000000) && (freq <= 54000000))) {
- da7213->mclk_rate = freq;
- return 0;
- } else {
- dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
- freq);
- return -EINVAL;
- }
+ 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(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+ 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 5MHz - 54MHz. */
-static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
- int source, unsigned int fref, unsigned int fout)
+/* 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 snd_soc_codec *codec = codec_dai->codec;
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+ 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;
- /* Reset PLL configuration */
- snd_soc_write(codec, DA7213_PLL_CTRL, 0);
-
- pll_ctrl = 0;
-
/* Workout input divider based on MCLK rate */
- if ((da7213->mclk_rate == 32768) && (source == DA7213_SYSCLK_PLL)) {
+ 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_10_20_MHZ;
- indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
+ 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;
- pll_ctrl |= DA7213_PLL_32K_MODE;
+
} else {
- /* 5 - 54MHz MCLK */
if (da7213->mclk_rate < 5000000) {
- goto pll_err;
- } else if (da7213->mclk_rate <= 10000000) {
- indiv_bits = DA7213_PLL_INDIV_5_10_MHZ;
- indiv = DA7213_PLL_INDIV_5_10_MHZ_VAL;
- } else if (da7213->mclk_rate <= 20000000) {
- indiv_bits = DA7213_PLL_INDIV_10_20_MHZ;
- indiv = DA7213_PLL_INDIV_10_20_MHZ_VAL;
- } else if (da7213->mclk_rate <= 40000000) {
- indiv_bits = DA7213_PLL_INDIV_20_40_MHZ;
- indiv = DA7213_PLL_INDIV_20_40_MHZ_VAL;
+ 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_40_54_MHZ;
- indiv = DA7213_PLL_INDIV_40_54_MHZ_VAL;
+ indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
+ indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- goto pll_err;
+ 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;
+ pll_ctrl = indiv_bits;
- /* PLL Bypass mode */
- if (source == DA7213_SYSCLK_MCLK) {
- snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ /* 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;
+ }
- /*
- * If Codec is slave and SRM enabled,
- * freq_out is (98304000 + 90316800)/2 = 94310400
- */
- if (!da7213->master && da7213->srm_en) {
+ pll_ctrl |= DA7213_PLL_32K_MODE | DA7213_PLL_SRM_EN;
fout = DA7213_PLL_FREQ_OUT_94310400;
- pll_ctrl |= DA7213_PLL_SRM_EN;
+ break;
+ default:
+ dev_err(component->dev, "Invalid PLL config\n");
+ return -EINVAL;
}
- /* Enable MCLK squarer if required */
- if (da7213->mclk_squarer_en)
- pll_ctrl |= DA7213_PLL_MCLK_SQR_EN;
-
/* Calculate dividers for PLL */
pll_integer = fout / freq_ref;
frac_div = (u64)(fout % freq_ref) * 8192ULL;
@@ -1319,29 +1689,61 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
pll_frac_bot = (frac_div) & DA7213_BYTE_MASK;
/* Write PLL dividers */
- snd_soc_write(codec, DA7213_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA7213_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA7213_PLL_INTEGER, pll_integer);
+ 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_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ 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;
-pll_err:
- dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n",
- da7213->mclk_rate);
- return -EINVAL;
+ return _da7213_set_component_pll(component, pll_id, source, fref, fout);
}
+/*
+ * Select below from Sound Card, not Auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 da7213_dai_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
/* DAI operations */
static const struct snd_soc_dai_ops da7213_dai_ops = {
.hw_params = da7213_hw_params,
.set_fmt = da7213_set_dai_fmt,
- .set_sysclk = da7213_set_dai_sysclk,
- .set_pll = da7213_set_dai_pll,
- .digital_mute = da7213_mute,
+ .mute_stream = da7213_mute,
+ .no_capture_mute = 1,
+ .auto_selectable_formats = &da7213_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver da7213_dai = {
@@ -1363,69 +1765,258 @@ static struct snd_soc_dai_driver da7213_dai = {
.formats = DA7213_FORMATS,
},
.ops = &da7213_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da7213_set_bias_level(struct snd_soc_codec *codec,
+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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA7213_REFERENCES,
+ 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_update_bits(codec, DA7213_REFERENCES,
+ snd_soc_component_update_bits(component, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int da7213_probe(struct snd_soc_codec *codec)
+#define DA7213_FIN_MIN_RATE (5 * MEGA)
+#define DA7212_FIN_MIN_RATE (2 * MEGA)
+
+#if defined(CONFIG_OF)
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+ { .compatible = "dlg,da7212", .data = (void *)DA7212_FIN_MIN_RATE },
+ { .compatible = "dlg,da7213", .data = (void *)DA7213_FIN_MIN_RATE },
+ { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+ { "DLGS7212", DA7212_FIN_MIN_RATE },
+ { "DLGS7213", DA7213_FIN_MIN_RATE },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
+
+static enum da7213_micbias_voltage
+ da7213_of_micbias_lvl(struct snd_soc_component *component, u32 val)
{
- struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
- struct da7213_platform_data *pdata = da7213->pdata;
+ 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_update_bits(codec, DA7213_ALC_CTRL1,
+ snd_soc_component_update_bits(component, DA7213_ALC_CTRL1,
DA7213_ALC_CALIB_MODE_MAN, 0);
da7213->alc_calib_auto = true;
- /* Default to using SRM for slave mode */
- da7213->srm_en = 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_update_bits(codec, DA7213_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_AUX_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_ADC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_DAC_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA7213_LINE_CTRL,
DA7213_GAIN_RAMP_EN, DA7213_GAIN_RAMP_EN);
/*
@@ -1436,26 +2027,32 @@ static int da7213_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA7213_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_L_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXIN_R_CTRL,
DA7213_MIXIN_MIX_EN, DA7213_MIXIN_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_L_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_MIXOUT_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MIXOUT_R_CTRL,
DA7213_MIXOUT_MIX_EN, DA7213_MIXOUT_MIX_EN);
- snd_soc_update_bits(codec, DA7213_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_L_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA7213_HP_R_CTRL,
DA7213_HP_AMP_OE, DA7213_HP_AMP_OE);
- snd_soc_update_bits(codec, DA7213_LINE_CTRL,
+ 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 */
@@ -1477,7 +2074,7 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_MICBIAS2_LEVEL_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MICBIAS_CTRL,
+ snd_soc_component_update_bits(component, DA7213_MICBIAS_CTRL,
DA7213_MICBIAS1_LEVEL_MASK |
DA7213_MICBIAS2_LEVEL_MASK, micbias_lvl);
@@ -1503,57 +2100,102 @@ static int da7213_probe(struct snd_soc_codec *codec)
DA7213_DMIC_CLK_RATE_SHIFT);
break;
}
- snd_soc_update_bits(codec, DA7213_MIC_CONFIG,
+ 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);
-
- /* Set MCLK squaring */
- da7213->mclk_squarer_en = pdata->mclk_squaring;
}
+
+ pm_runtime_put_sync(component->dev);
+
+ /* Check if MCLK provided */
+ da7213->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(da7213->mclk))
+ return PTR_ERR(da7213->mclk);
+ if (da7213->mclk)
+ /* 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;
+
+ /* Default infinite tone gen, start/stop by Kcontrol */
+ snd_soc_component_write(component, DA7213_TONE_GEN_CYCLES, DA7213_BEEP_CYCLES_MASK);
+
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da7213 = {
+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,
+ .max_register = DA7213_TONE_GEN_OFF_PER,
.reg_defaults = da7213_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(da7213_reg_defaults),
.volatile_reg = da7213_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
-static int da7213_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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;
- struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
- int ret;
+ int i, ret;
- da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
- GFP_KERNEL);
+ da7213 = devm_kzalloc(&i2c->dev, sizeof(*da7213), GFP_KERNEL);
if (!da7213)
return -ENOMEM;
- if (pdata)
- da7213->pdata = pdata;
+ da7213->fin_min_rate = (uintptr_t)i2c_get_match_data(i2c);
+ if (!da7213->fin_min_rate)
+ return -EINVAL;
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);
@@ -1561,23 +2203,55 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da7213, &da7213_dai, 1);
+ mutex_init(&da7213->ctrl_lock);
+
+ 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 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da7213 component: %d\n",
ret);
}
return ret;
}
-static int da7213_remove(struct i2c_client *client)
+static void da7213_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
+ pm_runtime_disable(&i2c->dev);
+}
+
+static int 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 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);
+ return regcache_sync(da7213->regmap);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(da7213_pm, da7213_runtime_suspend,
+ da7213_runtime_resume, NULL);
+
static const struct i2c_device_id da7213_i2c_id[] = {
- { "da7213", 0 },
+ { "da7213" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
@@ -1586,10 +2260,12 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(da7213_of_match),
+ .acpi_match_table = ACPI_PTR(da7213_acpi_match),
+ .pm = pm_ptr(&da7213_pm),
},
.probe = da7213_i2c_probe,
- .remove = da7213_remove,
+ .remove = da7213_i2c_remove,
.id_table = da7213_i2c_id,
};
@@ -1597,4 +2273,5 @@ module_i2c_driver(da7213_i2c_driver);
MODULE_DESCRIPTION("ASoC DA7213 Codec driver");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index 9cb9ddd01282..b9ab791d6b88 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -1,19 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* da7213.h - DA7213 ASoC Codec Driver
*
* Copyright (c) 2013 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
- *
- * 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.
+ * Author: David Rau <David.Rau.opensource@dm.renesas.com>
*/
#ifndef _DA7213_H
#define _DA7213_H
+#include <linux/clk.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <sound/da7213.h>
/*
@@ -136,11 +136,25 @@
#define DA7213_DAC_NG_ON_THRESHOLD 0xB1
#define DA7213_DAC_NG_CTRL 0xB2
+#define DA7213_TONE_GEN_CFG1 0xB4
+#define DA7213_TONE_GEN_CFG2 0xB5
+#define DA7213_TONE_GEN_CYCLES 0xB6
+#define DA7213_TONE_GEN_FREQ1_L 0xB7
+#define DA7213_TONE_GEN_FREQ1_U 0xB8
+#define DA7213_TONE_GEN_FREQ2_L 0xB9
+#define DA7213_TONE_GEN_FREQ2_U 0xBA
+#define DA7213_TONE_GEN_ON_PER 0xBB
+#define DA7213_TONE_GEN_OFF_PER 0xBC
/*
* Bit fields
*/
+#define DA7213_SWITCH_EN_MAX 0x1
+
+/* DA7213_PLL_STATUS = 0x03 */
+#define DA7213_PLL_SRM_LOCK (0x1 << 1)
+
/* DA7213_SR = 0x22 */
#define DA7213_SR_8000 (0x1 << 0)
#define DA7213_SR_11025 (0x2 << 0)
@@ -159,15 +173,16 @@
#define DA7213_VMID_EN (0x1 << 7)
/* DA7213_PLL_CTRL = 0x27 */
-#define DA7213_PLL_INDIV_5_10_MHZ (0x0 << 2)
-#define DA7213_PLL_INDIV_10_20_MHZ (0x1 << 2)
-#define DA7213_PLL_INDIV_20_40_MHZ (0x2 << 2)
-#define DA7213_PLL_INDIV_40_54_MHZ (0x3 << 2)
+#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)
@@ -176,21 +191,24 @@
#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_CLK_EN_SLAVE_MODE (0x0 << 7)
-#define DA7213_DAI_CLK_EN_MASTER_MODE (0x1 << 7)
+#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 */
@@ -411,6 +429,9 @@
#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
@@ -475,6 +496,55 @@
#define DA7213_DAC_NG_EN_SHIFT 7
#define DA7213_DAC_NG_EN_MAX 0x1
+/* DA7213_TONE_GEN_CFG1 = 0xB4 */
+#define DA7213_DTMF_REG_SHIFT 0
+#define DA7213_DTMF_REG_MASK (0xF << 0)
+#define DA7213_DTMF_REG_MAX 16
+#define DA7213_DTMF_EN_SHIFT 4
+#define DA7213_DTMF_EN_MASK (0x1 << 4)
+#define DA7213_START_STOPN_SHIFT 7
+#define DA7213_START_STOPN_MASK (0x1 << 7)
+
+/* DA7213_TONE_GEN_CFG2 = 0xB5 */
+#define DA7213_SWG_SEL_SHIFT 0
+#define DA7213_SWG_SEL_MASK (0x3 << 0)
+#define DA7213_SWG_SEL_MAX 4
+#define DA7213_SWG_SEL_SRAMP (0x3 << 0)
+#define DA7213_TONE_GEN_GAIN_SHIFT 4
+#define DA7213_TONE_GEN_GAIN_MASK (0xF << 4)
+#define DA7213_TONE_GEN_GAIN_MAX 0xF
+#define DA7213_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7213_TONE_GEN_GAIN_MINUS_15DB (0x5 << 4)
+
+/* DA7213_TONE_GEN_CYCLES = 0xB6 */
+#define DA7213_BEEP_CYCLES_SHIFT 0
+#define DA7213_BEEP_CYCLES_MASK (0x7 << 0)
+
+/* DA7213_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7213_FREQ1_L_SHIFT 0
+#define DA7213_FREQ1_L_MASK (0xFF << 0)
+#define DA7213_FREQ_MAX 0xFFFF
+
+/* DA7213_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7213_FREQ1_U_SHIFT 0
+#define DA7213_FREQ1_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7213_FREQ2_L_SHIFT 0
+#define DA7213_FREQ2_L_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7213_FREQ2_U_SHIFT 0
+#define DA7213_FREQ2_U_MASK (0xFF << 0)
+
+/* DA7213_TONE_GEN_ON_PER = 0xBB */
+#define DA7213_BEEP_ON_PER_SHIFT 0
+#define DA7213_BEEP_ON_PER_MASK (0x3F << 0)
+#define DA7213_BEEP_ON_OFF_MAX 0x3F
+
+/* DA7213_TONE_GEN_OFF_PER = 0xBC */
+#define DA7213_BEEP_OFF_PER_SHIFT 0
+#define DA7213_BEEP_OFF_PER_MASK (0x3F << 0)
/*
* General defines
@@ -494,30 +564,50 @@
#define DA7213_ALC_AVG_ITERATIONS 5
/* PLL related */
-#define DA7213_SYSCLK_MCLK 0
-#define DA7213_SYSCLK_PLL 1
-#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_10_MHZ_VAL 2
-#define DA7213_PLL_INDIV_10_20_MHZ_VAL 4
-#define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
-#define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
-
-enum clk_src {
- DA7213_CLKSRC_MCLK
+#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 mutex ctrl_lock;
+ struct regulator_bulk_data supplies[DA7213_NUM_SUPPLIES];
+ struct clk *mclk;
unsigned int mclk_rate;
+ unsigned int out_rate;
+ unsigned int fin_min_rate;
+ int clk_src;
bool master;
- bool mclk_squarer_en;
- bool srm_en;
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 000000000000..a7539e1a1893
--- /dev/null
+++ b/sound/soc/codecs/da7218.c
@@ -0,0 +1,3303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * da7218.c - DA7218 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7218.h>
+#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_CBP_CFP:
+ da7218->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 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 const 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 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);
+
+ da7218->dev_id = (uintptr_t)i2c_get_match_data(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 <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
new file mode 100644
index 000000000000..7f6a4aea2c7a
--- /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 <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef _DA7218_H
+#define _DA7218_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7218.h>
+
+
+/*
+ * 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 = 1,
+ 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 000000000000..15e5e3eb592b
--- /dev/null
+++ b/sound/soc/codecs/da7219-aad.c
@@ -0,0 +1,1080 @@
+// 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 <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/property.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#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) {
+ fwnode_handle_put(aad_np);
+ 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;
+
+ fwnode_handle_put(aad_np);
+
+ 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;
+
+ disable_irq(da7219_aad->irq);
+
+ if (da7219_aad->jack) {
+ /* Disable jack detection during suspend */
+ snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
+ /* Disable ground switch */
+ snd_soc_component_update_bits(component, 0xFB, 0x01, 0x00);
+
+ /*
+ * 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;
+ }
+ }
+ }
+}
+
+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);
+ }
+
+ enable_irq(da7219_aad->irq);
+}
+
+
+/*
+ * 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 <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
new file mode 100644
index 000000000000..fbfbf3e67918
--- /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 <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <linux/mutex.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * 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 000000000000..1742f91c788c
--- /dev/null
+++ b/sound/soc/codecs/da7219.c
@@ -0,0 +1,2732 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7219.h>
+#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;
+
+ mutex_lock(&da7219->pll_lock);
+
+ if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq)) {
+ mutex_unlock(&da7219->pll_lock);
+ return 0;
+ }
+
+ if ((freq < 2000000) || (freq > 54000000)) {
+ mutex_unlock(&da7219->pll_lock);
+ dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+ freq);
+ return -EINVAL;
+ }
+
+ 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_CBP_CFP:
+ da7219->master = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 int da7219_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_WCLK_IDX]);
+
+ if (!da7219->master)
+ return -EINVAL;
+
+ if (req->rate < 11025)
+ req->rate = 8000;
+ else if (req->rate < 12000)
+ req->rate = 11025;
+ else if (req->rate < 16000)
+ req->rate = 12000;
+ else if (req->rate < 22050)
+ req->rate = 16000;
+ else if (req->rate < 24000)
+ req->rate = 22050;
+ else if (req->rate < 32000)
+ req->rate = 24000;
+ else if (req->rate < 44100)
+ req->rate = 32000;
+ else if (req->rate < 48000)
+ req->rate = 44100;
+ else if (req->rate < 88200)
+ req->rate = 48000;
+ else if (req->rate < 96000)
+ req->rate = 88200;
+ else
+ req->rate = 96000;
+
+ return 0;
+}
+
+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 int da7219_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct da7219_priv *da7219 =
+ container_of(hw, struct da7219_priv,
+ dai_clks_hw[DA7219_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_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(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+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,
+ .determine_rate = da7219_wclk_determine_rate,
+ .set_rate = da7219_wclk_set_rate,
+ },
+ [DA7219_DAI_BCLK_IDX] = {
+ .recalc_rate = da7219_bclk_recalc_rate,
+ .determine_rate = da7219_bclk_determine_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 const 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 const 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 <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
new file mode 100644
index 000000000000..94af88f52589
--- /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 <Adam.Thomson.Opensource@diasemi.com>
+ */
+
+#ifndef __DA7219_H
+#define __DA7219_H
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * 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
index ffe96175a8a5..016c9be3ebda 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* da732x.c --- Dialog DA732X ALSA SoC Audio Driver
*
* Copyright (C) 2012 Dialog Semiconductor GmbH
*
* Author: Michal Hajduk <Michal.Hajduk@diasemi.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -43,7 +40,7 @@ struct da732x_priv {
/*
* da732x register cache - default settings
*/
-static struct reg_default da732x_reg_cache[] = {
+static const struct reg_default da732x_reg_cache[] = {
{ DA732X_REG_REF1 , 0x02 },
{ DA732X_REG_BIAS_EN , 0x80 },
{ DA732X_REG_BIAS1 , 0x00 },
@@ -168,52 +165,47 @@ static struct reg_default da732x_reg_cache[] = {
{ DA732X_REG_UNLOCK , 0x00 },
};
-static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk)
+static inline int da732x_get_input_div(struct snd_soc_component *component, int sysclk)
{
int val;
- int ret;
if (sysclk < DA732X_MCLK_10MHZ) {
- val = DA732X_MCLK_RET_0_10MHZ;
- ret = DA732X_MCLK_VAL_0_10MHZ;
+ val = DA732X_MCLK_VAL_0_10MHZ;
} else if ((sysclk >= DA732X_MCLK_10MHZ) &&
(sysclk < DA732X_MCLK_20MHZ)) {
- val = DA732X_MCLK_RET_10_20MHZ;
- ret = DA732X_MCLK_VAL_10_20MHZ;
+ val = DA732X_MCLK_VAL_10_20MHZ;
} else if ((sysclk >= DA732X_MCLK_20MHZ) &&
(sysclk < DA732X_MCLK_40MHZ)) {
- val = DA732X_MCLK_RET_20_40MHZ;
- ret = DA732X_MCLK_VAL_20_40MHZ;
+ val = DA732X_MCLK_VAL_20_40MHZ;
} else if ((sysclk >= DA732X_MCLK_40MHZ) &&
(sysclk <= DA732X_MCLK_54MHZ)) {
- val = DA732X_MCLK_RET_40_54MHZ;
- ret = DA732X_MCLK_VAL_40_54MHZ;
+ val = DA732X_MCLK_VAL_40_54MHZ;
} else {
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_PLL_CTRL, val);
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL, val);
- return ret;
+ return val;
}
-static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
+static void da732x_set_charge_pump(struct snd_soc_component *component, int state)
{
switch (state) {
case DA732X_ENABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
+ 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_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
DA732X_CP_CTRL_CPVDD1);
- snd_soc_write(codec, DA732X_REG_CP_CTRL2,
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL2,
DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
- snd_soc_write(codec, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
+ snd_soc_component_write(component, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
break;
case DA732X_DISABLE_CP:
- snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
- snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
- snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
+ 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");
@@ -331,10 +323,10 @@ static SOC_ENUM_SINGLE_DECL(da732x_adc2_voice_filter_enum,
static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
unsigned int bits;
switch (sel) {
@@ -351,7 +343,7 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits);
+ snd_soc_component_update_bits(component, reg, DA732X_HPF_MASK, bits);
return 0;
}
@@ -359,22 +351,22 @@ static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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_read(codec, reg) & DA732X_HPF_MASK;
+ val = snd_soc_component_read(component, reg) & DA732X_HPF_MASK;
switch (val) {
case DA732X_HPF_VOICE_EN:
- ucontrol->value.integer.value[0] = DA732X_HPF_VOICE;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_VOICE;
break;
case DA732X_HPF_MUSIC_EN:
- ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_MUSIC;
break;
default:
- ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED;
+ ucontrol->value.enumerated.item[0] = DA732X_HPF_DISABLED;
break;
}
@@ -609,18 +601,18 @@ static const struct snd_kcontrol_new da732x_snd_controls[] = {
static int da732x_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN,
DA732X_ADCC_BB_CLK_EN);
break;
@@ -628,24 +620,24 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
return -EINVAL;
}
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_RST_MASK,
DA732X_ADC_SET_ACT);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_ON);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+ snd_soc_component_update_bits(component, w->reg, DA732X_ADC_PD_MASK,
DA732X_ADC_OFF);
- snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+ 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_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCA_BB_CLK_EN, 0);
break;
case DA732X_REG_ADC2_PD:
- snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+ snd_soc_component_update_bits(component, DA732X_REG_CLK_EN3,
DA732X_ADCC_BB_CLK_EN, 0);
break;
default:
@@ -663,16 +655,16 @@ static int da732x_adc_event(struct snd_soc_dapm_widget *w,
static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift) | DA732X_OUT_HIZ_EN,
(1 << w->shift) | DA732X_OUT_HIZ_DIS);
break;
@@ -876,11 +868,11 @@ static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = {
static const struct snd_soc_dapm_route da732x_dapm_routes[] = {
/* Inputs */
- {"AUX1L PGA", "NULL", "AUX1L"},
- {"AUX1R PGA", "NULL", "AUX1R"},
+ {"AUX1L PGA", NULL, "AUX1L"},
+ {"AUX1R PGA", NULL, "AUX1R"},
{"MIC1 PGA", NULL, "MIC1"},
- {"MIC2 PGA", "NULL", "MIC2"},
- {"MIC3 PGA", "NULL", "MIC3"},
+ {"MIC2 PGA", NULL, "MIC2"},
+ {"MIC3 PGA", NULL, "MIC3"},
/* Capture Path */
{"ADC1 Left MUX", "MIC1", "MIC1 PGA"},
@@ -949,7 +941,7 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u32 aif = 0;
u32 reg_aif;
u32 fs;
@@ -1011,15 +1003,15 @@ static int da732x_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif);
- snd_soc_update_bits(codec, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u32 aif_mclk, pc_count;
u32 reg_aif1, aif1;
u32 reg_aif3, aif3;
@@ -1042,11 +1034,11 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
}
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
aif1 = DA732X_AIF_SLAVE;
aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif1 = DA732X_AIF_CLK_FROM_SRC;
aif_mclk = DA732X_CLK_GENERATION_AIF_A;
break;
@@ -1107,29 +1099,29 @@ static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
return -EINVAL;
}
- snd_soc_write(codec, DA732X_REG_AIF_MCLK, aif_mclk);
- snd_soc_update_bits(codec, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
- snd_soc_update_bits(codec, reg_aif3, DA732X_AIF_BCLK_INV |
+ 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_write(codec, DA732X_REG_PC_CTRL, pc_count);
+ snd_soc_component_write(component, DA732X_REG_PC_CTRL, pc_count);
return 0;
}
-static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
+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_codec_get_drvdata(codec);
+ 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_update_bits(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_EN, 0);
da732x->pll_en = false;
return 0;
@@ -1147,32 +1139,32 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
case 24576000:
case 45160000:
case 49152000:
- snd_soc_write(codec, DA732X_REG_PLL_CTRL,
+ snd_soc_component_write(component, DA732X_REG_PLL_CTRL,
DA732X_PLL_BYPASS);
return 0;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"Cannot use PLL Bypass, invalid SYSCLK rate\n");
return -EINVAL;
}
}
- indiv = da732x_get_input_div(codec, da732x->sysclk);
+ indiv = da732x_get_input_div(component, da732x->sysclk);
if (indiv < 0)
return indiv;
- fref = (da732x->sysclk / 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_write(codec, DA732X_REG_PLL_DIV_LO, div_lo);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_MID, div_mid);
- snd_soc_write(codec, DA732X_REG_PLL_DIV_HI, div_hi);
+ 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_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
DA732X_PLL_EN);
da732x->pll_en = true;
@@ -1183,8 +1175,8 @@ static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
da732x->sysclk = freq;
@@ -1196,13 +1188,7 @@ static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
#define DA732X_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_ops da732x_dai1_ops = {
- .hw_params = da732x_hw_params,
- .set_fmt = da732x_set_dai_fmt,
- .set_sysclk = da732x_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops da732x_dai2_ops = {
+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,
@@ -1227,7 +1213,7 @@ static struct snd_soc_dai_driver da732x_dai[] = {
.rates = DA732X_RATES,
.formats = DA732X_FORMATS,
},
- .ops = &da732x_dai1_ops,
+ .ops = &da732x_dai_ops,
},
{
.name = "DA732X_AIFB",
@@ -1247,7 +1233,7 @@ static struct snd_soc_dai_driver da732x_dai[] = {
.rates = DA732X_RATES,
.formats = DA732X_FORMATS,
},
- .ops = &da732x_dai2_ops,
+ .ops = &da732x_dai_ops,
},
};
@@ -1274,21 +1260,21 @@ static const struct regmap_config da732x_regmap = {
};
-static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
+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_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFFSET,
DA732X_HP_DAC_OFFSET_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPR_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_CALIBRATION |
DA732X_HP_DAC_OFF_SCALE_STEPS);
@@ -1296,9 +1282,9 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
msleep(DA732X_WAIT_FOR_STABILIZATION);
/* Check DAC offset sign */
- sign[DA732X_HPL_DAC] = (snd_soc_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ 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_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ 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) */
@@ -1308,17 +1294,17 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_DAC] |= step;
offset[DA732X_HPR_DAC] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ 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_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+ 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_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+ 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;
@@ -1326,19 +1312,19 @@ static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFFSET,
~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+ 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_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+ snd_soc_component_write(component, DA732X_REG_HPL_DAC_OFF_CNTL,
DA732X_HP_DAC_OFF_SCALE_STEPS);
- snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+ 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_codec *codec)
+static void da732x_output_offset_adjust(struct snd_soc_component *component)
{
u8 offset[DA732X_HP_AMPS];
u8 sign[DA732X_HP_AMPS];
@@ -1348,26 +1334,26 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
/* Initialize output offset calibration circuits and registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
- snd_soc_write(codec, DA732X_REG_HPL,
+ 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_write(codec, DA732X_REG_HPR,
+ 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_read(codec, DA732X_REG_HPL) &
+ sign[DA732X_HPL_AMP] = snd_soc_component_read(component, DA732X_REG_HPL) &
DA732X_HP_OUT_COMPO;
- sign[DA732X_HPR_AMP] = snd_soc_read(codec, DA732X_REG_HPR) &
+ sign[DA732X_HPR_AMP] = snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO;
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
+ 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_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
+ 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);
@@ -1375,17 +1361,17 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
do {
offset[DA732X_HPL_AMP] |= step;
offset[DA732X_HPR_AMP] |= step;
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPL_OUT_OFFSET,
offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET,
+ snd_soc_component_write(component, DA732X_REG_HPR_OUT_OFFSET,
offset[DA732X_HPR_AMP]);
msleep(DA732X_WAIT_FOR_STABILIZATION);
- if ((snd_soc_read(codec, DA732X_REG_HPL) &
+ 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_read(codec, DA732X_REG_HPR) &
+ if ((snd_soc_component_read(component, DA732X_REG_HPR) &
DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
offset[DA732X_HPR_AMP] &= ~step;
@@ -1393,80 +1379,80 @@ static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
} while (step);
/* Write final DAC offsets to registers */
- snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
- snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+ 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_codec *codec)
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_component *component)
{
/* Make sure that we have Soft Mute enabled */
- snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+ snd_soc_component_write(component, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
+ 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_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
+ snd_soc_component_write(component, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
- snd_soc_write(codec, DA732X_REG_HPR, 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(codec);
- da732x_output_offset_adjust(codec);
+ da732x_dac_offset_adjust(component);
+ da732x_output_offset_adjust(component);
- snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
- snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_DIS);
- snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_DIS);
+ 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_codec *codec,
+static int da732x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+ struct da732x_priv *da732x = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Init Codec */
- snd_soc_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_VMID_FASTCHG);
- snd_soc_write(codec, DA732X_REG_BIAS_EN,
+ 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_write(codec, DA732X_REG_REF1,
+ snd_soc_component_write(component, DA732X_REG_REF1,
DA732X_REFBUFX2_EN);
/* Enable bypass DSP routing */
- snd_soc_write(codec, DA732X_REG_DATA_ROUTE,
+ snd_soc_component_write(component, DA732X_REG_DATA_ROUTE,
DA732X_BYPASS_DSP);
/* Enable Digital subsystem */
- snd_soc_write(codec, DA732X_REG_DSP_CTRL,
+ snd_soc_component_write(component, DA732X_REG_DSP_CTRL,
DA732X_DIGITAL_EN);
- snd_soc_write(codec, DA732X_REG_SPARE1_OUT,
+ snd_soc_component_write(component, DA732X_REG_SPARE1_OUT,
DA732X_HP_DRIVER_EN |
DA732X_HP_GATE_LOW |
DA732X_HP_LOOP_GAIN_CTRL);
- snd_soc_write(codec, DA732X_REG_HP_LIN1_GNDSEL,
+ snd_soc_component_write(component, DA732X_REG_HP_LIN1_GNDSEL,
DA732X_HP_OUT_GNDSEL);
- da732x_set_charge_pump(codec, DA732X_ENABLE_CP);
+ da732x_set_charge_pump(component, DA732X_ENABLE_CP);
- snd_soc_write(codec, DA732X_REG_CLK_EN1,
+ snd_soc_component_write(component, DA732X_REG_CLK_EN1,
DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
/* Enable Zero Crossing */
- snd_soc_write(codec, DA732X_REG_INP_ZC_EN,
+ snd_soc_component_write(component, DA732X_REG_INP_ZC_EN,
DA732X_MIC1_PRE_ZC_EN |
DA732X_MIC1_ZC_EN |
DA732X_MIC2_PRE_ZC_EN |
@@ -1475,39 +1461,37 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
DA732X_AUXR_ZC_EN |
DA732X_MIC3_PRE_ZC_EN |
DA732X_MIC3_ZC_EN);
- snd_soc_write(codec, DA732X_REG_OUT_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(codec);
+ da732x_hp_dc_offset_cancellation(component);
regcache_cache_only(da732x->regmap, false);
regcache_sync(da732x->regmap);
} else {
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+ snd_soc_component_update_bits(component, DA732X_REG_BIAS_EN,
DA732X_BIAS_BOOST_MASK,
DA732X_BIAS_BOOST_50PC);
- snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+ 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(codec, DA732X_DISABLE_CP);
- snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
+ 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;
}
- codec->dapm.bias_level = level;
-
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da732x = {
+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),
@@ -1516,10 +1500,12 @@ static struct snd_soc_codec_driver soc_codec_dev_da732x = {
.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,
- const struct i2c_device_id *id)
+static int da732x_i2c_probe(struct i2c_client *i2c)
{
struct da732x_priv *da732x;
unsigned int reg;
@@ -1549,24 +1535,18 @@ static int da732x_i2c_probe(struct i2c_client *i2c,
(reg & DA732X_ID_MAJOR_MASK) >> 4,
(reg & DA732X_ID_MINOR_MASK));
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x,
+ 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 codec.\n");
+ dev_err(&i2c->dev, "Failed to register component.\n");
err:
return ret;
}
-static int da732x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id da732x_i2c_id[] = {
- { "da7320", 0},
+ { "da7320"},
{ }
};
MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
@@ -1574,10 +1554,8 @@ MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
static struct i2c_driver da732x_i2c_driver = {
.driver = {
.name = "da7320",
- .owner = THIS_MODULE,
},
.probe = da732x_i2c_probe,
- .remove = da732x_i2c_remove,
.id_table = da732x_i2c_id,
};
diff --git a/sound/soc/codecs/da732x.h b/sound/soc/codecs/da732x.h
index f586cbd30b77..c2f784c3f359 100644
--- a/sound/soc/codecs/da732x.h
+++ b/sound/soc/codecs/da732x.h
@@ -1,13 +1,10 @@
+/* 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 <Michal.Hajduk@diasemi.com>
- *
- * 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 __DA732X_H_
@@ -51,14 +48,10 @@
#define DA732X_MCLK_20MHZ 20000000
#define DA732X_MCLK_40MHZ 40000000
#define DA732X_MCLK_54MHZ 54000000
-#define DA732X_MCLK_RET_0_10MHZ 0
-#define DA732X_MCLK_VAL_0_10MHZ 1
-#define DA732X_MCLK_RET_10_20MHZ 1
-#define DA732X_MCLK_VAL_10_20MHZ 2
-#define DA732X_MCLK_RET_20_40MHZ 2
-#define DA732X_MCLK_VAL_20_40MHZ 4
-#define DA732X_MCLK_RET_40_54MHZ 3
-#define DA732X_MCLK_VAL_40_54MHZ 8
+#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
diff --git a/sound/soc/codecs/da732x_reg.h b/sound/soc/codecs/da732x_reg.h
index bdd03ca4b2de..a493e0b46f5d 100644
--- a/sound/soc/codecs/da732x_reg.h
+++ b/sound/soc/codecs/da732x_reg.h
@@ -1,13 +1,10 @@
+/* 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 <Michal.Hajduk@diasemi.com>
- *
- * 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 __DA732X_REG_H_
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index ad19cc56702b..eb795abe9acd 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA9055 ALSA Soc codec driver
*
@@ -6,11 +7,6 @@
* Tested on (Samsung SMDK6410 board + DA9055 EVB) using I2S and I2C
* Written by David Chen <david.chen@diasemi.com> and
* Ashish Chavan <ashish.chavan@kpitcummins.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#include <linux/delay.h>
@@ -19,7 +15,6 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/of_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -289,26 +284,23 @@ enum clk_src {
/* Gain and Volume */
-static const unsigned int aux_vol_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int digital_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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 unsigned int alc_analog_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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);
@@ -454,7 +446,7 @@ static const char * const da9055_hold_time_txt[] = {
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_codec *codec, u8 reg_val)
+static int da9055_get_alc_data(struct snd_soc_component *component, u8 reg_val)
{
int mid_data, top_data;
int sum = 0;
@@ -463,17 +455,17 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
for (iteration = 0; iteration < DA9055_ALC_AVG_ITERATIONS;
iteration++) {
/* Select the left or right channel and capture data */
- snd_soc_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL, reg_val);
+ 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_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_MIDDLE);
- mid_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ 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_write(codec, DA9055_ALC_CIC_OP_LVL_CTRL,
+ snd_soc_component_write(component, DA9055_ALC_CIC_OP_LVL_CTRL,
reg_val | DA9055_ALC_DATA_TOP);
- top_data = snd_soc_read(codec, DA9055_ALC_CIC_OP_LVL_DATA);
+ top_data = snd_soc_component_read(component, DA9055_ALC_CIC_OP_LVL_DATA);
sum += ((mid_data << 8) | (top_data << 16));
}
@@ -484,7 +476,7 @@ static int da9055_get_alc_data(struct snd_soc_codec *codec, u8 reg_val)
static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ 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;
@@ -495,31 +487,31 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
*/
/* Save current values from Mic control registers */
- mic_left = snd_soc_read(codec, DA9055_MIC_L_CTRL);
- mic_right = snd_soc_read(codec, DA9055_MIC_R_CTRL);
+ 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_update_bits(codec, DA9055_MIC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIC_L_CTRL,
DA9055_MIC_L_MUTE_EN, DA9055_MIC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_MIC_R_CTRL,
+ 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_read(codec, DA9055_ADC_L_CTRL);
- adc_right = snd_soc_read(codec, DA9055_ADC_R_CTRL);
+ 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_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_ADC_L_EN, DA9055_ADC_L_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ 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(codec,
+ avg_left_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_LEFT);
/* Right Data */
- avg_right_data = da9055_get_alc_data(codec,
+ avg_right_data = da9055_get_alc_data(component,
DA9055_ALC_CIC_OP_CHANNEL_RIGHT);
/* Calculate DC offset */
@@ -527,22 +519,22 @@ static int da9055_put_alc_sw(struct snd_kcontrol *kcontrol,
offset_r = -avg_right_data;
reg_val = (offset_l & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_L, reg_val);
reg_val = (offset_l & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_L, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_L, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_15_8) >> 8;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2M_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2M_R, reg_val);
reg_val = (offset_r & DA9055_ALC_OFFSET_17_16) >> 16;
- snd_soc_write(codec, DA9055_ALC_OFFSET_OP2U_R, reg_val);
+ snd_soc_component_write(component, DA9055_ALC_OFFSET_OP2U_R, reg_val);
/* Restore original values of ADC control registers */
- snd_soc_write(codec, DA9055_ADC_L_CTRL, adc_left);
- snd_soc_write(codec, DA9055_ADC_R_CTRL, adc_right);
+ 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_write(codec, DA9055_MIC_L_CTRL, mic_left);
- snd_soc_write(codec, DA9055_MIC_R_CTRL, mic_right);
+ 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);
@@ -948,7 +940,7 @@ struct da9055_priv {
struct da9055_platform_data *pdata;
};
-static struct reg_default da9055_reg_defaults[] = {
+static const struct reg_default da9055_reg_defaults[] = {
{ 0x21, 0x10 },
{ 0x22, 0x0A },
{ 0x23, 0x00 },
@@ -1044,9 +1036,9 @@ static bool da9055_volatile_register(struct device *dev,
case DA9055_HP_R_GAIN_STATUS:
case DA9055_LINE_GAIN_STATUS:
case DA9055_ALC_CIC_OP_LVL_DATA:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -1055,8 +1047,8 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
u8 aif_ctrl, fs;
u32 sysclk;
@@ -1078,7 +1070,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
}
/* Set AIF format */
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
+ snd_soc_component_update_bits(component, DA9055_AIF_CTRL, DA9055_AIF_WORD_LENGTH_MASK,
aif_ctrl);
switch (params_rate(params)) {
@@ -1128,7 +1120,7 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
if (da9055->mclk_rate) {
/* PLL Mode, Write actual FS */
- snd_soc_write(codec, DA9055_SR, fs);
+ snd_soc_component_write(component, DA9055_SR, fs);
} else {
/*
* Non-PLL Mode
@@ -1137,24 +1129,24 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
* 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_write(codec, DA9055_SR, DA9055_SR_48000);
+ 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_update_bits(codec, DA9055_PLL_CTRL,
+ 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_update_bits(codec, DA9055_PLL_CTRL,
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL,
DA9055_PLL_EN, DA9055_PLL_EN);
}
} else {
/* Non PLL Mode, disable PLL */
- snd_soc_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ snd_soc_component_update_bits(component, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
}
return 0;
@@ -1163,17 +1155,17 @@ static int da9055_hw_params(struct snd_pcm_substream *substream,
/* Set DAI mode and Format */
static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
/* DA9055 in I2S Master Mode */
mode = 1;
aif_clk_mode = DA9055_AIF_CLK_EN_MASTER_MODE;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
/* DA9055 in I2S Slave Mode */
mode = 0;
aif_clk_mode = DA9055_AIF_CLK_EN_SLAVE_MODE;
@@ -1183,7 +1175,7 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* Don't allow change of mode if PLL is enabled */
- if ((snd_soc_read(codec, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
+ if ((snd_soc_component_read(component, DA9055_PLL_CTRL) & DA9055_PLL_EN) &&
(da9055->master != mode))
return -EINVAL;
@@ -1210,27 +1202,27 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
/* By default only 32 BCLK per WCLK is supported */
aif_clk_mode |= DA9055_AIF_BCLKS_PER_WCLK_32;
- snd_soc_update_bits(codec, DA9055_AIF_CLK_MODE,
+ snd_soc_component_update_bits(component, DA9055_AIF_CLK_MODE,
(DA9055_AIF_CLK_MODE_MASK | DA9055_AIF_BCLK_MASK),
aif_clk_mode);
- snd_soc_update_bits(codec, DA9055_AIF_CTRL, DA9055_AIF_FORMAT_MASK,
+ 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)
+static int da9055_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
if (mute) {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, DA9055_DAC_L_MUTE_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, DA9055_DAC_R_MUTE_EN);
} else {
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_DAC_L_MUTE_EN, 0);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_DAC_R_MUTE_EN, 0);
}
@@ -1243,8 +1235,8 @@ static int da9055_mute(struct snd_soc_dai *dai, int mute)
static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1286,13 +1278,13 @@ static int da9055_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, DA9055_PLL_CTRL, DA9055_PLL_EN, 0);
+ 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))
@@ -1315,9 +1307,9 @@ static int da9055_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
goto pll_err;
/* Write PLL dividers */
- snd_soc_write(codec, DA9055_PLL_FRAC_TOP, pll_frac_top);
- snd_soc_write(codec, DA9055_PLL_FRAC_BOT, pll_frac_bot);
- snd_soc_write(codec, DA9055_PLL_INTEGER, pll_integer);
+ 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:
@@ -1331,7 +1323,8 @@ static const struct snd_soc_dai_ops da9055_dai_ops = {
.set_fmt = da9055_set_dai_fmt,
.set_sysclk = da9055_set_dai_sysclk,
.set_pll = da9055_set_dai_pll,
- .digital_mute = da9055_mute,
+ .mute_stream = da9055_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver da9055_dai = {
@@ -1353,10 +1346,10 @@ static struct snd_soc_dai_driver da9055_dai = {
.formats = DA9055_FORMATS,
},
.ops = &da9055_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int da9055_set_bias_level(struct snd_soc_codec *codec,
+static int da9055_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -1364,49 +1357,48 @@ static int da9055_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
- snd_soc_update_bits(codec, DA9055_REFERENCES,
+ 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_update_bits(codec, DA9055_REFERENCES,
+ snd_soc_component_update_bits(component, DA9055_REFERENCES,
DA9055_VMID_EN | DA9055_BIAS_EN, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int da9055_probe(struct snd_soc_codec *codec)
+static int da9055_probe(struct snd_soc_component *component)
{
- struct da9055_priv *da9055 = snd_soc_codec_get_drvdata(codec);
+ struct da9055_priv *da9055 = snd_soc_component_get_drvdata(component);
/* Enable all Gain Ramps */
- snd_soc_update_bits(codec, DA9055_AUX_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_AUX_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_AUX_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_ADC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_ADC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_DAC_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_DAC_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_L_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_HP_R_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
- snd_soc_update_bits(codec, DA9055_LINE_CTRL,
+ snd_soc_component_update_bits(component, DA9055_LINE_CTRL,
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
/*
@@ -1416,28 +1408,28 @@ static int da9055_probe(struct snd_soc_codec *codec)
* being managed by DAPM while other (non power related) bits are
* enabled here
*/
- snd_soc_update_bits(codec, DA9055_MIXIN_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_L_CTRL,
DA9055_MIXIN_L_MIX_EN, DA9055_MIXIN_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXIN_R_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_CTRL,
DA9055_MIXIN_R_MIX_EN, DA9055_MIXIN_R_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_L_CTRL,
+ snd_soc_component_update_bits(component, DA9055_MIXOUT_L_CTRL,
DA9055_MIXOUT_L_MIX_EN, DA9055_MIXOUT_L_MIX_EN);
- snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
+ 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_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
+ 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_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN,
DA9055_MICBIAS2_EN);
} else {
- snd_soc_update_bits(codec, DA9055_MIXIN_R_SELECT,
+ snd_soc_component_update_bits(component, DA9055_MIXIN_R_SELECT,
DA9055_MICBIAS2_EN, 0);
}
/* set mic bias voltage */
@@ -1446,7 +1438,7 @@ static int da9055_probe(struct snd_soc_codec *codec)
case DA9055_MICBIAS_2_1V:
case DA9055_MICBIAS_1_8V:
case DA9055_MICBIAS_1_6V:
- snd_soc_update_bits(codec, DA9055_MIC_CONFIG,
+ snd_soc_component_update_bits(component, DA9055_MIC_CONFIG,
DA9055_MICBIAS_LEVEL_MASK,
(da9055->pdata->micbias) << 4);
break;
@@ -1455,17 +1447,18 @@ static int da9055_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_da9055 = {
+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 = {
@@ -1478,8 +1471,7 @@ static const struct regmap_config da9055_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int da9055_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int da9055_i2c_probe(struct i2c_client *i2c)
{
struct da9055_priv *da9055;
struct da9055_platform_data *pdata = dev_get_platdata(&i2c->dev);
@@ -1502,21 +1494,15 @@ static int da9055_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_da9055, &da9055_dai, 1);
+ 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 codec: %d\n",
+ dev_err(&i2c->dev, "Failed to register da9055 component: %d\n",
ret);
}
return ret;
}
-static int da9055_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
/*
* 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
@@ -1525,25 +1511,26 @@ static int da9055_remove(struct i2c_client *client)
* and PMIC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
- { "da9055-codec", 0 },
+ { "da9055-codec" },
{ }
};
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",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(da9055_of_match),
},
.probe = da9055_i2c_probe,
- .remove = da9055_remove,
.id_table = da9055_i2c_id,
};
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index fde53251c047..61e1bf1b3c9e 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -1,25 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* dmic.c -- SoC audio for Generic Digital MICs
*
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -27,6 +17,75 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#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;
+ struct regulator *vref;
+ 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);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dmic->gpio_en)
+ gpiod_set_value_cansleep(dmic->gpio_en, 1);
+
+ if (dmic->vref) {
+ ret = regulator_enable(dmic->vref);
+ if (ret)
+ return ret;
+ }
+
+ 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);
+
+ if (dmic->vref)
+ ret = regulator_disable(dmic->vref);
+
+ break;
+ }
+
+ return ret;
+}
+
static struct snd_soc_dai_driver dmic_dai = {
.name = "dmic-hifi",
.capture = {
@@ -36,13 +95,58 @@ static struct snd_soc_dai_driver dmic_dai = {
.rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = SNDRV_PCM_FMTBIT_S32_LE
| SNDRV_PCM_FMTBIT_S24_LE
- | SNDRV_PCM_FMTBIT_S16_LE,
+ | SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U8
+ | SNDRV_PCM_FMTBIT_DSD_U16_LE
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE
+ | SNDRV_PCM_FMTBIT_DSD_U16_BE
+ | SNDRV_PCM_FMTBIT_DSD_U32_BE,
},
+ .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->vref = devm_regulator_get_optional(component->dev, "vref");
+ if (IS_ERR(dmic->vref)) {
+ if (PTR_ERR(dmic->vref) != -ENODEV)
+ return dev_err_probe(component->dev, PTR_ERR(dmic->vref),
+ "Failed to get vref\n");
+ dmic->vref = NULL;
+ }
+
+ 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("DMIC AIF", "Capture", 0,
- SND_SOC_NOPM, 0, 0),
+ 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"),
};
@@ -50,33 +154,59 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"},
};
-static struct snd_soc_codec_driver soc_dmic = {
- .dapm_widgets = dmic_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
- .dapm_routes = intercon,
- .num_dapm_routes = ARRAY_SIZE(intercon),
+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)
{
- return snd_soc_register_codec(&pdev->dev,
- &soc_dmic, &dmic_dai, 1);
-}
+ int err;
+ u32 chans;
+ struct snd_soc_dai_driver *dai_drv = &dmic_dai;
-static int dmic_dev_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ 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,
- .remove = dmic_dev_remove,
};
module_platform_driver(dmic_driver);
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
new file mode 100644
index 000000000000..f5150d2f95da
--- /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 <jbrunet@baylibre.com>
+ */
+
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * 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 <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c
new file mode 100644
index 000000000000..339553cfbb48
--- /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 <jbrunet@baylibre.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+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 <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c
new file mode 100644
index 000000000000..f557e33c26ad
--- /dev/null
+++ b/sound/soc/codecs/es8311.c
@@ -0,0 +1,973 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#include "linux/array_size.h"
+#include "sound/pcm.h"
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "es8311.h"
+
+#define ES8311_NUM_RATES 10
+#define ES8311_RATES (SNDRV_PCM_RATE_8000_96000)
+#define ES8311_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)
+
+struct es8311_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned long mclk_freq;
+ bool provider;
+ unsigned int rates[ES8311_NUM_RATES];
+ struct snd_pcm_hw_constraint_list constraints;
+};
+
+static const DECLARE_TLV_DB_SCALE(es8311_adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_pga_gain_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8311_adc_scale_tlv, 0, 600, 0);
+
+#define ES8311_DB_LRCK_STEPS \
+ "0.25db/4LRCK", \
+ "0.25db/8LRCK", \
+ "0.25db/16LRCK", \
+ "0.25db/32LRCK", \
+ "0.25db/64LRCK", \
+ "0.25db/128LRCK", \
+ "0.25db/256LRCK", \
+ "0.25db/512LRCK", \
+ "0.25db/1024LRCK", \
+ "0.25db/2048LRCK", \
+ "0.25db/4096LRCK", \
+ "0.25db/8192LRCK", \
+ "0.25db/16384LRCK", \
+ "0.25db/32768LRCK", \
+ "0.25db/65536LRCK",
+
+static const char *const es8311_level_winsize_txt[] = {
+ "0.25db/2LRCK",
+ ES8311_DB_LRCK_STEPS
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ es8311_alc_winsize, ES8311_ADC4,
+ ES8311_ADC4_ALC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_level_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(-3010, 600, 0),
+ 2, 3, TLV_DB_SCALE_ITEM(-2060, 250, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(-1610, 160, 0),
+ 6, 7, TLV_DB_SCALE_ITEM(-1320, 120, 0),
+ 8, 9, TLV_DB_SCALE_ITEM(-1100, 90, 0),
+ 10, 11, TLV_DB_SCALE_ITEM(-930, 80, 0),
+ 12, 15, TLV_DB_SCALE_ITEM(-780, 60, 0),
+);
+
+static const char *const es8311_ramprate_txt[] = {
+ "Disabled",
+ ES8311_DB_LRCK_STEPS
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_adc_ramprate, ES8311_ADC1,
+ ES8311_ADC1_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_automute_winsize_txt[] = {
+ "2048 samples",
+ "4096 samples",
+ "6144 samples",
+ "8192 samples",
+ "10240 samples",
+ "12288 samples",
+ "14336 samples",
+ "16384 samples",
+ "18432 samples",
+ "20480 samples",
+ "22528 samples",
+ "24576 samples",
+ "26624 samples",
+ "28672 samples",
+ "30720 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_automute_winsize, ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_WS_SHIFT, es8311_automute_winsize_txt);
+static const DECLARE_TLV_DB_RANGE(es8311_automute_ng_tlv,
+ 0, 7, TLV_DB_SCALE_ITEM(-9600, 600, 0),
+ 8, 15, TLV_DB_SCALE_ITEM(-5100, 300, 0),
+);
+static const DECLARE_TLV_DB_SCALE(es8311_automute_vol_tlv, -2800, 400, 0);
+
+static const DECLARE_TLV_DB_SCALE(es8311_dac_vol_tlv, -9550, 50, 0);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_drc_winsize, ES8311_DAC4,
+ ES8311_DAC4_DRC_WINSIZE_SHIFT, es8311_level_winsize_txt);
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_ramprate, ES8311_DAC6,
+ ES8311_DAC6_RAMPRATE_SHIFT, es8311_ramprate_txt);
+
+static const char *const es8311_out_mode_txt[] = {
+ "Lineout",
+ "Headphones"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_out_mode, ES8311_SYS9,
+ ES8311_SYS9_HPSW_SHIFT, es8311_out_mode_txt);
+
+static const struct snd_kcontrol_new es8311_snd_controls[] = {
+ /* Capture path */
+ SOC_SINGLE_TLV("PGA Capture Volume", ES8311_SYS10,
+ ES8311_SYS10_PGAGAIN_SHIFT, ES8311_SYS10_PGAGAIN_MAX, 0,
+ es8311_pga_gain_tlv),
+ SOC_SINGLE("ADC Polarity Invert Capture Switch", ES8311_ADC2,
+ ES8311_ADC2_INV_SHIFT, 1, 0),
+ SOC_SINGLE_TLV("ADC Scale Capture Volume", ES8311_ADC2,
+ ES8311_ADC2_SCALE_SHIFT, ES8311_ADC2_SCALE_MAX, 0,
+ es8311_adc_scale_tlv),
+ SOC_SINGLE_TLV("ADC Capture Volume", ES8311_ADC3,
+ ES8311_ADC3_VOLUME_SHIFT, ES8311_ADC3_VOLUME_MAX, 0,
+ es8311_adc_vol_tlv),
+ SOC_ENUM("ADC Capture Ramp Rate", es8311_adc_ramprate),
+ SOC_SINGLE("ADC Automute Capture Switch", ES8311_ADC4,
+ ES8311_ADC4_AUTOMUTE_EN_SHIFT, 1, 0),
+ SOC_ENUM("ADC Automute Capture Winsize", es8311_automute_winsize),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate Capture Volume", ES8311_ADC6,
+ ES8311_ADC6_AUTOMUTE_NG_SHIFT,
+ ES8311_ADC6_AUTOMUTE_NG_MAX, 0, es8311_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Capture Volume", ES8311_ADC7,
+ ES8311_ADC7_AUTOMUTE_VOL_SHIFT,
+ ES8311_ADC7_AUTOMUTE_VOL_MAX, 0,
+ es8311_automute_vol_tlv),
+ SOC_SINGLE("ADC HPF Capture Switch", ES8311_ADC8, ES8311_ADC8_HPF_SHIFT,
+ 1, 0),
+ SOC_SINGLE("ADC EQ Capture Switch", ES8311_ADC8,
+ ES8311_ADC8_EQBYPASS_SHIFT, 1, 1),
+ SOC_SINGLE("ALC Capture Switch", ES8311_ADC4, ES8311_ADC4_ALC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("ALC Capture Max Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MAXLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("ALC Capture Min Volume", ES8311_ADC5,
+ ES8311_ADC5_ALC_MINLEVEL_SHIFT,
+ ES8311_ADC5_ALC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("ALC Capture Winsize", es8311_alc_winsize),
+
+ /* Playback path */
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8311_DAC2, 0,
+ ES8311_DAC2_VOLUME_MAX, 0, es8311_dac_vol_tlv),
+ SOC_SINGLE("DRC Playback Switch", ES8311_DAC4, ES8311_DAC4_DRC_EN_SHIFT,
+ 1, 0),
+ SOC_SINGLE_TLV("DRC Playback Max Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MAXLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MAXLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_SINGLE_TLV("DRC Playback Min Volume", ES8311_DAC5,
+ ES8311_DAC5_DRC_MINLEVEL_SHIFT,
+ ES8311_DAC5_DRC_MINLEVEL_MAX, 0, es8311_level_tlv),
+ SOC_ENUM("DRC Playback Winsize", es8311_drc_winsize),
+ SOC_ENUM("DAC Playback Ramp Rate", es8311_dac_ramprate),
+ SOC_SINGLE("DAC EQ Playback Switch", ES8311_DAC6,
+ ES8311_DAC6_EQBYPASS_SHIFT, 1, 1),
+
+ SOC_ENUM("Output Mode", es8311_out_mode),
+};
+
+static const char *const es8311_diff_src_txt[] = {
+ "Disabled",
+ "MIC1P-MIC1N",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_diff_src_enum, ES8311_SYS10,
+ ES8311_SYS10_LINESEL_SHIFT, es8311_diff_src_txt);
+static const struct snd_kcontrol_new es8311_diff_src_mux =
+ SOC_DAPM_ENUM("Differential Source", es8311_diff_src_enum);
+
+static const char *const es8311_dmic_src_txt[] = {
+ "Disabled",
+ "DMIC from MIC1P",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dmic_src_enum, ES8311_SYS10,
+ ES8311_SYS10_DMIC_ON_SHIFT, es8311_dmic_src_txt);
+static const struct snd_kcontrol_new es8311_dmic_src_mux =
+ SOC_DAPM_ENUM("Digital Mic Source", es8311_dmic_src_enum);
+
+static const char * const es8311_aif1tx_src_txt[] = {
+ "ADC + ADC",
+ "ADC + 0",
+ "0 + ADC",
+ "0 + 0",
+ "DACL + ADC",
+ "ADC + DACR",
+ "DACL + DACR",
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_aif1tx_src_enum, ES8311_GPIO,
+ ES8311_GPIO_ADCDAT_SEL_SHIFT, es8311_aif1tx_src_txt);
+static const struct snd_kcontrol_new es8311_aif1tx_src_mux =
+ SOC_DAPM_ENUM("AIF1TX Source", es8311_aif1tx_src_enum);
+
+static const char * const es8311_dac_src_txt[] = {
+ "Left",
+ "Right"
+};
+static SOC_ENUM_SINGLE_DECL(
+ es8311_dac_src_enum, ES8311_SDP_IN,
+ ES8311_SDP_IN_SEL_SHIFT, es8311_dac_src_txt);
+static const struct snd_kcontrol_new es8311_dac_src_mux =
+ SOC_DAPM_ENUM("Mono DAC Source", es8311_dac_src_enum);
+
+static const struct snd_soc_dapm_widget es8311_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Bias", ES8311_SYS3, ES8311_SYS3_PDN_IBIASGEN_SHIFT,
+ 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Analog power", ES8311_SYS3,
+ ES8311_SYS3_PDN_ANA_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Vref", ES8311_SYS3, ES8311_SYS3_PDN_VREF_SHIFT, 1,
+ NULL, 0),
+
+ /* Capture path */
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_diff_src_mux),
+ SND_SOC_DAPM_SUPPLY("ADC Bias Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCBIASGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_ADCVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKADC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGA", ES8311_SYS4, ES8311_SYS4_PDN_PGA_SHIFT, 1, NULL,
+ 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, ES8311_SYS4,
+ ES8311_SYS4_PDN_MOD_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Digital Mic Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dmic_src_mux),
+ SND_SOC_DAPM_MUX("AIF1TX Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_aif1tx_src_mux),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8311_SDP_OUT,
+ ES8311_SDP_MUTE_SHIFT, 1),
+
+ /* Playback path */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8311_SDP_IN,
+ ES8311_SDP_MUTE_SHIFT, 1),
+ SND_SOC_DAPM_MUX("Mono DAC Source Mux", SND_SOC_NOPM, 0, 0,
+ &es8311_dac_src_mux),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, ES8311_SYS8,
+ ES8311_SYS8_PDN_DAC_SHIFT, 1),
+ SND_SOC_DAPM_SUPPLY("DAC Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_CLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Analog Clock", ES8311_CLKMGR1,
+ ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC Vref Gen", ES8311_SYS3,
+ ES8311_SYS3_PDN_DACVREFGEN_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8311_dapm_routes[] = {
+ /* Capture Path */
+ { "MIC1", NULL, "Bias" },
+ { "MIC1", NULL, "Analog power" },
+ { "MIC1", NULL, "Vref" },
+ { "Differential Mux", "MIC1P-MIC1N", "MIC1" },
+ { "PGA", NULL, "Differential Mux" },
+ { "Mono ADC", NULL, "PGA" },
+ { "Mono ADC", NULL, "ADC Bias Gen" },
+ { "Mono ADC", NULL, "ADC Vref Gen" },
+ { "Mono ADC", NULL, "ADC Clock" },
+ { "Mono ADC", NULL, "ADC Analog Clock" },
+ { "Digital Mic Mux", "Disabled", "Mono ADC" },
+ { "Digital Mic Mux", "DMIC from MIC1P", "DMIC" },
+
+ { "AIF1TX Source Mux", "ADC + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + 0", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "0 + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "DACL + ADC", "Digital Mic Mux" },
+ { "AIF1TX Source Mux", "ADC + DACR", "Digital Mic Mux" },
+
+ { "AIF1TX", NULL, "AIF1TX Source Mux" },
+
+ /* Playback Path */
+ { "Mono DAC Source Mux", "Left", "AIF1RX" },
+ { "Mono DAC Source Mux", "Right", "AIF1RX" },
+ { "Mono DAC", NULL, "Mono DAC Source Mux" },
+ { "Mono DAC", NULL, "DAC Clock" },
+ { "Mono DAC", NULL, "DAC Analog Clock" },
+ { "OUT", NULL, "Mono DAC" },
+ { "OUT", NULL, "Bias" },
+ { "OUT", NULL, "Analog power" },
+ { "OUT", NULL, "Vref" },
+ { "OUT", NULL, "DAC Vref Gen" },
+};
+
+/* Bit clock divider values:
+ * from 1 to 20: the register takes the div value - 1
+ * above 20: the register takes the corresponding idx of the div value
+ * in the following table + 20
+ */
+#define ES8311_BCLK_DIV_IDX_OFFSET 20
+static const unsigned int es8311_bclk_divs[] = {
+ 22, 24, 25, 30, 32, 33, 34, 36, 44, 48, 66, 72
+};
+
+struct es8311_mclk_coeff {
+ unsigned int rate;
+ unsigned int mclk;
+ unsigned int div;
+ unsigned int mult;
+ unsigned int div_adc_dac;
+};
+
+#define ES8311_MCLK_MAX_FREQ 49200000
+
+/* Coefficients for common master clock frequencies based on clock table from
+ * documentation. Limited to have a ratio of adc (or dac) clock to lrclk equal
+ * to 256. This to keep the default adc and dac oversampling and adc scale
+ * settings. Internal mclk dividers and multipliers are dynamically adjusted to
+ * support, respectively, multiples (up to x8) and factors (/2,4,8) of listed
+ * mclks frequencies (see es8311_cmp_adj_mclk_coeff).
+ * All rates are supported when mclk/rate ratio is 32, 64, 128, 256, 384 or 512
+ * (upper limit due to max mclk freq of 49.2MHz).
+ */
+static const struct es8311_mclk_coeff es8311_mclk_coeffs[] = {
+ { 8000, 2048000, 1, 1, 1 },
+ { 8000, 6144000, 3, 1, 1 },
+ { 8000, 18432000, 3, 1, 3 },
+ { 11025, 2822400, 1, 1, 1 },
+ { 11025, 8467200, 3, 1, 1 },
+ { 16000, 4096000, 1, 1, 1 },
+ { 16000, 12288000, 3, 1, 1 },
+ { 16000, 18432000, 3, 2, 3 },
+ { 22050, 5644800, 1, 1, 1 },
+ { 22050, 16934400, 3, 1, 1 },
+ { 32000, 8192000, 1, 1, 1 },
+ { 32000, 12288000, 3, 2, 1 },
+ { 32000, 18432000, 3, 4, 3 },
+ { 44100, 11289600, 1, 1, 1 },
+ { 44100, 33868800, 3, 1, 1 },
+ { 48000, 12288000, 1, 1, 1 },
+ { 48000, 18432000, 3, 2, 1 },
+ { 64000, 8192000, 1, 2, 1 },
+ { 64000, 12288000, 3, 4, 1 },
+ { 88200, 11289600, 1, 2, 1 },
+ { 88200, 33868800, 3, 2, 1 },
+ { 96000, 12288000, 1, 2, 1 },
+ { 96000, 18432000, 3, 4, 1 },
+};
+
+/* Compare coeff with provided mclk_freq and adjust it if needed.
+ * If frequencies match, return 0 and the unaltered coeff copy into out_coeff.
+ * If mclk_freq is a valid multiple or factor of coeff mclk freq, return 0 and
+ * the adjusted coeff copy into out_coeff.
+ * Return -EINVAL otherwise.
+ */
+static int es8311_cmp_adj_mclk_coeff(unsigned int mclk_freq,
+ const struct es8311_mclk_coeff *coeff,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ if (WARN_ON_ONCE(!coeff))
+ return -EINVAL;
+
+ unsigned int div = coeff->div;
+ unsigned int mult = coeff->mult;
+ bool match = false;
+
+ if (coeff->mclk == mclk_freq) {
+ match = true;
+ } else if (mclk_freq % coeff->mclk == 0) {
+ div = mclk_freq / coeff->mclk;
+ div *= coeff->div;
+ if (div <= 8)
+ match = true;
+ } else if (coeff->mclk % mclk_freq == 0) {
+ mult = coeff->mclk / mclk_freq;
+ if (mult == 2 || mult == 4 || mult == 8) {
+ mult *= coeff->mult;
+ if (mult <= 8)
+ match = true;
+ }
+ }
+ if (!match)
+ return -EINVAL;
+ if (out_coeff) {
+ *out_coeff = *coeff;
+ out_coeff->div = div;
+ out_coeff->mult = mult;
+ }
+ return 0;
+}
+
+static int es8311_get_mclk_coeff(unsigned int mclk_freq, unsigned int rate,
+ struct es8311_mclk_coeff *out_coeff)
+{
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (coeff->rate != rate)
+ continue;
+
+ int ret =
+ es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, out_coeff);
+ if (ret == 0)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static void es8311_set_sysclk_constraints(unsigned int mclk_freq,
+ struct es8311_priv *es8311)
+{
+ unsigned int count = 0;
+
+ for (unsigned int i = 0; i < ARRAY_SIZE(es8311_mclk_coeffs) &&
+ count < ARRAY_SIZE(es8311->rates); i++) {
+ const struct es8311_mclk_coeff *coeff = &es8311_mclk_coeffs[i];
+
+ if (count > 0 && coeff->rate == es8311->rates[count - 1])
+ continue;
+
+ int ret = es8311_cmp_adj_mclk_coeff(mclk_freq, coeff, NULL);
+ if (ret == 0)
+ es8311->rates[count++] = coeff->rate;
+ }
+ if (count) {
+ es8311->constraints.list = es8311->rates;
+ es8311->constraints.count = count;
+ }
+}
+
+static int es8311_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ unsigned int mask = ES8311_DAC1_DAC_DSMMUTE |
+ ES8311_DAC1_DAC_DEMMUTE;
+ unsigned int val = mute ? mask : 0;
+
+ regmap_update_bits(es8311->regmap, ES8311_DAC1, mask, val);
+ }
+
+ return 0;
+}
+
+static int es8311_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (es8311->constraints.list) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &es8311->constraints);
+ }
+
+ return 0;
+}
+
+static int es8311_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 es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+ unsigned int wl;
+ int par_width = params_width(params);
+
+ switch (par_width) {
+ case 16:
+ wl = ES8311_SDP_WL_16;
+ break;
+ case 18:
+ wl = ES8311_SDP_WL_18;
+ break;
+ case 20:
+ wl = ES8311_SDP_WL_20;
+ break;
+ case 24:
+ wl = ES8311_SDP_WL_24;
+ break;
+ case 32:
+ wl = ES8311_SDP_WL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ unsigned int width = (unsigned int)par_width;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ snd_soc_component_update_bits(component, ES8311_SDP_IN,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ } else {
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT,
+ ES8311_SDP_WL_MASK,
+ wl << ES8311_SDP_WL_SHIFT);
+ }
+
+ if (es8311->mclk_freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "mclk frequency %lu too high\n",
+ es8311->mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int mclk_freq = es8311->mclk_freq;
+ unsigned int rate = params_rate(params);
+ unsigned int clkmgr = ES8311_CLKMGR1_MCLK_ON;
+
+ if (!mclk_freq) {
+ if (es8311->provider) {
+ dev_err(component->dev,
+ "mclk not configured, cannot run as master\n");
+ return -EINVAL;
+ }
+ dev_dbg(component->dev,
+ "mclk not configured, use bclk as internal mclk\n");
+
+ clkmgr = ES8311_CLKMGR1_MCLK_SEL;
+
+ mclk_freq = rate * width * 2;
+ }
+
+ struct es8311_mclk_coeff coeff;
+ int ret = es8311_get_mclk_coeff(mclk_freq, rate, &coeff);
+ if (ret) {
+ dev_err(component->dev, "unable to find mclk coefficient\n");
+ return ret;
+ }
+
+ unsigned int mask = ES8311_CLKMGR1_MCLK_SEL | ES8311_CLKMGR1_MCLK_ON |
+ ES8311_CLKMGR1_BCLK_ON;
+
+ clkmgr |= ES8311_CLKMGR1_BCLK_ON;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR1, mask, clkmgr);
+
+ if (WARN_ON_ONCE(coeff.div == 0 || coeff.div > 8 ||
+ coeff.div_adc_dac == 0 || coeff.div_adc_dac > 8))
+ return -EINVAL;
+
+ unsigned int mult;
+
+ switch (coeff.mult) {
+ case 1:
+ mult = 0;
+ break;
+ case 2:
+ mult = 1;
+ break;
+ case 4:
+ mult = 2;
+ break;
+ case 8:
+ mult = 3;
+ break;
+ default:
+ WARN_ON_ONCE(true);
+ return -EINVAL;
+ }
+
+ mask = ES8311_CLKMGR2_DIV_PRE_MASK | ES8311_CLKMGR2_MULT_PRE_MASK;
+ clkmgr = (coeff.div - 1) << ES8311_CLKMGR2_DIV_PRE_SHIFT |
+ mult << ES8311_CLKMGR2_MULT_PRE_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR2, mask, clkmgr);
+
+ mask = ES8311_CLKMGR5_ADC_DIV_MASK | ES8311_CLKMGR5_DAC_DIV_MASK;
+ clkmgr = (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_ADC_DIV_SHIFT |
+ (coeff.div_adc_dac - 1) << ES8311_CLKMGR5_DAC_DIV_SHIFT;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR5, mask, clkmgr);
+
+ if (es8311->provider) {
+ unsigned int div_lrclk = mclk_freq / rate;
+
+ if (WARN_ON_ONCE(div_lrclk == 0 ||
+ div_lrclk > ES8311_CLKMGR_LRCLK_DIV_MAX + 1))
+ return -EINVAL;
+
+ mask = ES8311_CLKMGR7_LRCLK_DIV_H_MASK;
+ clkmgr = (div_lrclk - 1) >> 8;
+ snd_soc_component_update_bits(component, ES8311_CLKMGR7, mask,
+ clkmgr);
+ clkmgr = (div_lrclk - 1) & 0xFF;
+ snd_soc_component_write(component, ES8311_CLKMGR8, clkmgr);
+
+ if (div_lrclk % (2 * width) != 0) {
+ dev_err(component->dev,
+ "unable to divide mclk %u to generate bclk\n",
+ mclk_freq);
+ return -EINVAL;
+ }
+
+ unsigned int div_bclk = div_lrclk / (2 * width);
+
+ mask = ES8311_CLKMGR6_DIV_BCLK_MASK;
+ if (div_bclk <= ES8311_BCLK_DIV_IDX_OFFSET) {
+ clkmgr = div_bclk - 1;
+ } else {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8311_bclk_divs); i++) {
+ if (es8311_bclk_divs[i] == div_bclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(es8311_bclk_divs)) {
+ dev_err(component->dev,
+ "bclk divider %u not supported\n",
+ div_bclk);
+ return -EINVAL;
+ }
+
+ clkmgr = i + ES8311_BCLK_DIV_IDX_OFFSET;
+ }
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask,
+ clkmgr);
+ }
+
+ return 0;
+}
+
+static int es8311_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 es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ if (freq > ES8311_MCLK_MAX_FREQ) {
+ dev_err(component->dev, "invalid frequency %u: too high\n",
+ freq);
+ return -EINVAL;
+ }
+
+ if (es8311->mclk_freq == freq)
+ return 0;
+
+ es8311->mclk_freq = freq;
+ es8311->constraints.list = NULL;
+ es8311->constraints.count = 0;
+
+ if (freq == 0)
+ return 0;
+
+ int ret = clk_set_rate(es8311->mclk, freq);
+ if (ret) {
+ dev_err(component->dev, "unable to set mclk rate\n");
+ return ret;
+ }
+
+ es8311_set_sysclk_constraints(freq, es8311);
+
+ return ret;
+}
+
+static int es8311_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct es8311_priv *es8311 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Master mode */
+ es8311->provider = true;
+
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC,
+ ES8311_RESET_MSC);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Slave mode */
+ es8311->provider = false;
+ snd_soc_component_update_bits(component, ES8311_RESET,
+ ES8311_RESET_MSC, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int sdp = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ sdp |= ES8311_SDP_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ sdp |= ES8311_SDP_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "right justified mode not supported\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_DSP_B:
+ sdp |= ES8311_SDP_LRP;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_A:
+ sdp |= ES8311_SDP_FMT_DSP;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ default:
+ dev_err(component->dev,
+ "inverted fsync not supported in dsp mode\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int clkmgr = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ sdp |= ES8311_SDP_LRP;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ clkmgr |= ES8311_CLKMGR6_BCLK_INV;
+ sdp |= ES8311_SDP_LRP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ unsigned int mask = ES8311_CLKMGR6_BCLK_INV;
+
+ snd_soc_component_update_bits(component, ES8311_CLKMGR6, mask, clkmgr);
+
+ mask = ES8311_SDP_FMT_MASK | ES8311_SDP_LRP;
+ snd_soc_component_update_bits(component, ES8311_SDP_IN, mask, sdp);
+ snd_soc_component_update_bits(component, ES8311_SDP_OUT, mask, sdp);
+
+ return 0;
+}
+
+static int es8311_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8311_priv *es8311 = 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) {
+ int ret = clk_prepare_enable(es8311->mclk);
+ if (ret) {
+ dev_err(component->dev,
+ "unable to prepare mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3,
+ ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED);
+ }
+
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8311->mclk);
+ snd_soc_component_update_bits(
+ component, ES8311_SYS3, ES8311_SYS3_PDN_VMIDSEL_MASK,
+ ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8311_dai_ops = {
+ .startup = es8311_startup,
+ .hw_params = es8311_hw_params,
+ .mute_stream = es8311_mute,
+ .set_sysclk = es8311_set_sysclk,
+ .set_fmt = es8311_set_dai_fmt,
+ .no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver es8311_dai = {
+ .name = "es8311",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = ES8311_RATES,
+ .formats = ES8311_FORMATS,
+ },
+ .ops = &es8311_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8311_reset(struct snd_soc_component *component, bool reset)
+{
+ /* Reset procedure:
+ * (1) power down state machine and reset codec blocks then,
+ * (2) after a short delay, power up state machine and leave reset mode.
+ * Specific delay is not documented, using the same as es8316.
+ */
+ unsigned int mask = ES8311_RESET_CSM_ON | ES8311_RESET_RST_MASK;
+
+ if (reset) {
+ /* Enter reset mode */
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_RST_MASK);
+ } else {
+ /* Leave reset mode */
+ usleep_range(5000, 5500);
+ snd_soc_component_update_bits(component, ES8311_RESET, mask,
+ ES8311_RESET_CSM_ON);
+ }
+}
+
+static int es8311_suspend(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, true);
+
+ regcache_cache_only(es8311->regmap, true);
+ regcache_mark_dirty(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_resume(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311_reset(component, false);
+
+ regcache_cache_only(es8311->regmap, false);
+ regcache_sync(es8311->regmap);
+
+ return 0;
+}
+
+static int es8311_component_probe(struct snd_soc_component *component)
+{
+ struct es8311_priv *es8311;
+
+ es8311 = snd_soc_component_get_drvdata(component);
+
+ es8311->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8311->mclk)) {
+ dev_err(component->dev, "invalid mclk\n");
+ return PTR_ERR(es8311->mclk);
+ }
+
+ es8311->mclk_freq = clk_get_rate(es8311->mclk);
+ if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ)
+ es8311_set_sysclk_constraints(es8311->mclk_freq, es8311);
+
+ es8311_reset(component, true);
+ es8311_reset(component, false);
+
+ /* Set minimal power up time */
+ snd_soc_component_write(component, ES8311_SYS1, 0);
+ snd_soc_component_write(component, ES8311_SYS2, 0);
+
+ return 0;
+}
+
+static const struct regmap_config es8311_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8311_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct snd_soc_component_driver es8311_component_driver = {
+ .probe = es8311_component_probe,
+ .suspend = es8311_suspend,
+ .resume = es8311_resume,
+ .set_bias_level = es8311_set_bias_level,
+ .controls = es8311_snd_controls,
+ .num_controls = ARRAY_SIZE(es8311_snd_controls),
+ .dapm_widgets = es8311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8311_dapm_widgets),
+ .dapm_routes = es8311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8311_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int es8311_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8311_priv *es8311;
+
+ struct device *dev = &i2c_client->dev;
+
+ es8311 = devm_kzalloc(dev, sizeof(*es8311), GFP_KERNEL);
+ if (es8311 == NULL)
+ return -ENOMEM;
+
+ es8311->regmap =
+ devm_regmap_init_i2c(i2c_client, &es8311_regmap_config);
+ if (IS_ERR(es8311->regmap))
+ return PTR_ERR(es8311->regmap);
+
+ i2c_set_clientdata(i2c_client, es8311);
+
+ return devm_snd_soc_register_component(dev, &es8311_component_driver,
+ &es8311_dai, 1);
+}
+
+static const struct i2c_device_id es8311_id[] = {
+ { "es8311" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8311_id);
+
+static const struct of_device_id es8311_of_match[] = {
+ {
+ .compatible = "everest,es8311",
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, es8311_of_match);
+
+static struct i2c_driver es8311_i2c_driver = {
+ .driver = {
+ .name = "es8311",
+ .of_match_table = es8311_of_match,
+ },
+ .probe = es8311_i2c_probe,
+ .id_table = es8311_id,
+};
+
+module_i2c_driver(es8311_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8311 driver");
+MODULE_AUTHOR("Matteo Martelli <matteomartelli3@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8311.h b/sound/soc/codecs/es8311.h
new file mode 100644
index 000000000000..8a3105bb8443
--- /dev/null
+++ b/sound/soc/codecs/es8311.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8311.c -- es8311 ALSA SoC audio driver
+ *
+ * Copyright (C) 2024 Matteo Martelli <matteomartelli3@gmail.com>
+ *
+ * Author: Matteo Martelli <matteomartelli3@gmail.com>
+ */
+
+#ifndef _ES8311_H
+#define _ES8311_H
+
+#include <linux/bitops.h>
+
+#define ES8311_RESET 0x00
+#define ES8311_RESET_CSM_ON BIT(7)
+#define ES8311_RESET_MSC BIT(6)
+#define ES8311_RESET_RST_MASK GENMASK(4, 0)
+
+/* Clock Manager Registers */
+#define ES8311_CLKMGR1 0x01
+#define ES8311_CLKMGR1_MCLK_SEL BIT(7)
+#define ES8311_CLKMGR1_MCLK_ON BIT(5)
+#define ES8311_CLKMGR1_BCLK_ON BIT(4)
+#define ES8311_CLKMGR1_CLKADC_ON_SHIFT 3
+#define ES8311_CLKMGR1_CLKDAC_ON_SHIFT 2
+#define ES8311_CLKMGR1_ANACLKADC_ON_SHIFT 1
+#define ES8311_CLKMGR1_ANACLKDAC_ON_SHIFT 0
+#define ES8311_CLKMGR2 0x02
+#define ES8311_CLKMGR2_DIV_PRE_MASK GENMASK(7, 5)
+#define ES8311_CLKMGR2_DIV_PRE_SHIFT 5
+#define ES8311_CLKMGR2_DIV_PRE_MAX 0x07
+#define ES8311_CLKMGR2_MULT_PRE_MASK GENMASK(4, 3)
+#define ES8311_CLKMGR2_MULT_PRE_SHIFT 3
+#define ES8311_CLKMGR3 0x03
+#define ES8311_CLKMGR4 0x04
+#define ES8311_CLKMGR5 0x05
+#define ES8311_CLKMGR5_ADC_DIV_MASK GENMASK(7, 4)
+#define ES8311_CLKMGR5_ADC_DIV_SHIFT 4
+#define ES8311_CLKMGR5_DAC_DIV_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR5_DAC_DIV_SHIFT 0
+#define ES8311_CLKMGR6 0x06
+#define ES8311_CLKMGR6_BCLK_INV BIT(5)
+#define ES8311_CLKMGR6_DIV_BCLK_MASK GENMASK(4, 0)
+#define ES8311_CLKMGR7 0x07
+#define ES8311_CLKMGR7_LRCLK_DIV_H_MASK GENMASK(3, 0)
+#define ES8311_CLKMGR8 0x08
+#define ES8311_CLKMGR_LRCLK_DIV_MAX 0x0FFF
+
+/* SDP Mode Registers */
+#define ES8311_SDP_IN 0x09
+#define ES8311_SDP_IN_SEL_SHIFT 7
+#define ES8311_SDP_OUT 0x0A
+/* Following values are the same for both SPD_IN and SDP_OUT */
+#define ES8311_SDP_MUTE_SHIFT 6
+#define ES8311_SDP_LRP BIT(5)
+#define ES8311_SDP_WL_MASK GENMASK(4, 2)
+#define ES8311_SDP_WL_SHIFT 2
+#define ES8311_SDP_WL_24 0x00
+#define ES8311_SDP_WL_20 0x01
+#define ES8311_SDP_WL_18 0x02
+#define ES8311_SDP_WL_16 0x03
+#define ES8311_SDP_WL_32 0x04
+#define ES8311_SDP_FMT_MASK GENMASK(1, 0)
+#define ES8311_SDP_FMT_I2S 0x00
+#define ES8311_SDP_FMT_LEFT_J 0x01
+#define ES8311_SDP_FMT_DSP 0x03
+
+/* System registers */
+#define ES8311_SYS1 0x0B
+#define ES8311_SYS2 0x0C
+#define ES8311_SYS3 0x0D
+#define ES8311_SYS3_PDN_ANA_SHIFT 7
+#define ES8311_SYS3_PDN_IBIASGEN_SHIFT 6
+#define ES8311_SYS3_PDN_ADCBIASGEN_SHIFT 5
+#define ES8311_SYS3_PDN_ADCVREFGEN_SHIFT 4
+#define ES8311_SYS3_PDN_DACVREFGEN_SHIFT 3
+#define ES8311_SYS3_PDN_VREF_SHIFT 2
+#define ES8311_SYS3_PDN_VMIDSEL_MASK GENMASK(1, 0)
+#define ES8311_SYS3_PDN_VMIDSEL_POWER_DOWN 0
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_NORMAL_SPEED 1
+#define ES8311_SYS3_PDN_VMIDSEL_NORMAL_OPERATION 2
+#define ES8311_SYS3_PDN_VMIDSEL_STARTUP_FAST_SPEED 3
+#define ES8311_SYS4 0x0E
+#define ES8311_SYS4_PDN_PGA_SHIFT 6
+#define ES8311_SYS4_PDN_MOD_SHIFT 5
+#define ES8311_SYS5 0x0F
+#define ES8311_SYS6 0x10
+#define ES8311_SYS7 0x11
+#define ES8311_SYS8 0x12
+#define ES8311_SYS8_PDN_DAC_SHIFT 1
+#define ES8311_SYS9 0x13
+#define ES8311_SYS9_HPSW_SHIFT 4
+#define ES8311_SYS10 0x14
+#define ES8311_SYS10_DMIC_ON_SHIFT 6
+#define ES8311_SYS10_LINESEL_SHIFT 4
+#define ES8311_SYS10_PGAGAIN_SHIFT 0
+#define ES8311_SYS10_PGAGAIN_MAX 0x0A
+
+/* ADC Registers*/
+#define ES8311_ADC1 0x15
+#define ES8311_ADC1_RAMPRATE_SHIFT 4
+#define ES8311_ADC2 0x16
+#define ES8311_ADC2_INV_SHIFT 4
+#define ES8311_ADC2_SCALE_SHIFT 0
+#define ES8311_ADC2_SCALE_MAX 0x07
+#define ES8311_ADC3 0x17
+#define ES8311_ADC3_VOLUME_SHIFT 0
+#define ES8311_ADC3_VOLUME_MAX 0xFF
+#define ES8311_ADC4 0x18
+#define ES8311_ADC4_ALC_EN_SHIFT 7
+#define ES8311_ADC4_AUTOMUTE_EN_SHIFT 6
+#define ES8311_ADC4_ALC_WINSIZE_SHIFT 0
+#define ES8311_ADC5 0x19
+#define ES8311_ADC5_ALC_MAXLEVEL_SHIFT 4
+#define ES8311_ADC5_ALC_MAXLEVEL_MAX 0x0F
+#define ES8311_ADC5_ALC_MINLEVEL_SHIFT 0
+#define ES8311_ADC5_ALC_MINLEVEL_MAX 0x0F
+#define ES8311_ADC6 0x1A
+#define ES8311_ADC6_AUTOMUTE_WS_SHIFT 4
+#define ES8311_ADC6_AUTOMUTE_NG_SHIFT 0
+#define ES8311_ADC6_AUTOMUTE_NG_MAX 0x0F
+
+#define ES8311_ADC7 0x1B
+#define ES8311_ADC7_AUTOMUTE_VOL_SHIFT 5
+#define ES8311_ADC7_AUTOMUTE_VOL_MAX 0x07
+#define ES8311_ADC8 0x1C
+#define ES8311_ADC8_EQBYPASS_SHIFT 6
+#define ES8311_ADC8_HPF_SHIFT 5
+
+/* DAC Registers */
+#define ES8311_DAC1 0x31
+#define ES8311_DAC1_DAC_DSMMUTE BIT(6)
+#define ES8311_DAC1_DAC_DEMMUTE BIT(5)
+#define ES8311_DAC2 0x32
+#define ES8311_DAC2_VOLUME_MAX 0xFF
+#define ES8311_DAC3 0x33
+#define ES8311_DAC4 0x34
+#define ES8311_DAC4_DRC_EN_SHIFT 7
+#define ES8311_DAC4_DRC_WINSIZE_SHIFT 0
+#define ES8311_DAC5 0x35
+#define ES8311_DAC5_DRC_MAXLEVEL_SHIFT 4
+#define ES8311_DAC5_DRC_MAXLEVEL_MAX 0x0F
+#define ES8311_DAC5_DRC_MINLEVEL_SHIFT 0
+#define ES8311_DAC5_DRC_MINLEVEL_MAX 0x0F
+#define ES8311_DAC6 0x37
+#define ES8311_DAC6_RAMPRATE_SHIFT 4
+#define ES8311_DAC6_EQBYPASS_SHIFT 3
+
+/* GPIO Registers */
+#define ES8311_GPIO 0x44
+#define ES8311_GPIO_ADC2DAC_SEL_SHIFT 7
+#define ES8311_GPIO_ADCDAT_SEL_SHIFT 4
+
+/* Chip Info Registers */
+#define ES8311_CHIPID1 0xFD /* 0x83 */
+#define ES8311_CHIPID2 0xFE /* 0x11 */
+#define ES8311_CHIPVER 0xFF
+
+#define ES8311_REG_MAX 0xFF
+
+#endif
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
new file mode 100644
index 000000000000..e7bd561a8f40
--- /dev/null
+++ b/sound/soc/codecs/es8316.c
@@ -0,0 +1,933 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8316.c -- es8316 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>,
+ * Daniel Drake <drake@endlessm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#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).
+ */
+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;
+ /* ES83xx supports halving the MCLK so it supports twice as many rates
+ */
+ unsigned int allowed_rates[ARRAY_SIZE(supported_mclk_lrck_ratios) * 2];
+ 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, 3, 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 < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (freq % ratio == 0)
+ es8316->allowed_rates[count++] = freq / ratio;
+
+ /* We also check if the halved MCLK produces a valid rate
+ * since the codec supports halving the MCLK.
+ */
+ if ((freq / ratio) % 2 == 0)
+ es8316->allowed_rates[count++] = freq / ratio / 2;
+ }
+
+ 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;
+ unsigned int clk = es8316->sysclk / 2;
+ bool clk_valid = false;
+
+ /* We will start with halved sysclk and see if we can use it
+ * for proper clocking. This is to minimise the risk of running
+ * the CODEC with a too high frequency. We have an SKU where
+ * the sysclk frequency is 48Mhz and this causes the sound to be
+ * sped up. If we can run with a halved sysclk, we will use it,
+ * if we can't use it, then full sysclk will be used.
+ */
+ do {
+ /* Validate supported sample rates that are autodetected from MCLK */
+ for (i = 0; i < ARRAY_SIZE(supported_mclk_lrck_ratios); i++) {
+ const unsigned int ratio = supported_mclk_lrck_ratios[i];
+
+ if (clk % ratio != 0)
+ continue;
+ if (clk / ratio == params_rate(params))
+ break;
+ }
+ if (i == ARRAY_SIZE(supported_mclk_lrck_ratios)) {
+ if (clk == es8316->sysclk)
+ return -EINVAL;
+ clk = es8316->sysclk;
+ } else {
+ clk_valid = true;
+ }
+ } while (!clk_valid);
+
+ if (clk != es8316->sysclk) {
+ snd_soc_component_update_bits(component, ES8316_CLKMGR_CLKSW,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV,
+ ES8316_CLKMGR_CLKSW_MCLK_DIV);
+ }
+
+ lrck_divider = clk / 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 | SNDRV_PCM_FMTBIT_S32_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" },
+ {}
+};
+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 <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/es8316.h b/sound/soc/codecs/es8316.h
new file mode 100644
index 000000000000..0ff16f948690
--- /dev/null
+++ b/sound/soc/codecs/es8316.h
@@ -0,0 +1,135 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Author: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#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
+
+/* ES8316_CLKMGR_CLKSW */
+#define ES8316_CLKMGR_CLKSW_MCLK_DIV 0x80
+
+#endif
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
new file mode 100644
index 000000000000..eb85b71e87f3
--- /dev/null
+++ b/sound/soc/codecs/es8323.c
@@ -0,0 +1,791 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8323.c -- es8323 ALSA SoC audio driver
+//
+// Copyright 2024 Rockchip Electronics Co. Ltd.
+// Copyright 2024 Everest Semiconductor Co.,Ltd.
+// Copyright 2024 Loongson Technology Co.,Ltd.
+//
+// Author: Mark Brown <broonie@kernel.org>
+// Jianqun Xu <jay.xu@rock-chips.com>
+// Nickey Yang <nickey.yang@rock-chips.com>
+// Further cleanup and restructuring by:
+// Binbin Zhou <zhoubinbin@loongson.cn>
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "es8323.h"
+
+struct es8323_priv {
+ unsigned int sysclk;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct snd_pcm_hw_constraint_list *sysclk_constraints;
+ struct snd_soc_component *component;
+};
+
+/* es8323 register cache */
+static const struct reg_default es8323_reg_defaults[] = {
+ { ES8323_CONTROL1, 0x06 },
+ { ES8323_CONTROL2, 0x1c },
+ { ES8323_CHIPPOWER, 0xc3 },
+ { ES8323_ADCPOWER, 0xfc },
+ { ES8323_DACPOWER, 0xc0 },
+ { ES8323_CHIPLOPOW1, 0x00 },
+ { ES8323_CHIPLOPOW2, 0x00 },
+ { ES8323_ANAVOLMANAG, 0x7c },
+ { ES8323_MASTERMODE, 0x80 },
+ { ES8323_ADCCONTROL1, 0x00 },
+ { ES8323_ADCCONTROL2, 0x00 },
+ { ES8323_ADCCONTROL3, 0x06 },
+ { ES8323_ADCCONTROL4, 0x00 },
+ { ES8323_ADCCONTROL5, 0x06 },
+ { ES8323_ADCCONTROL6, 0x30 },
+ { ES8323_ADC_MUTE, 0x30 },
+ { ES8323_LADC_VOL, 0xc0 },
+ { ES8323_RADC_VOL, 0xc0 },
+ { ES8323_ADCCONTROL10, 0x38 },
+ { ES8323_ADCCONTROL11, 0xb0 },
+ { ES8323_ADCCONTROL12, 0x32 },
+ { ES8323_ADCCONTROL13, 0x06 },
+ { ES8323_ADCCONTROL14, 0x00 },
+ { ES8323_DACCONTROL1, 0x00 },
+ { ES8323_DACCONTROL2, 0x06 },
+ { ES8323_DAC_MUTE, 0x30 },
+ { ES8323_LDAC_VOL, 0xc0 },
+ { ES8323_RDAC_VOL, 0xc0 },
+ { ES8323_DACCONTROL6, 0x08 },
+ { ES8323_DACCONTROL7, 0x06 },
+ { ES8323_DACCONTROL8, 0x1f },
+ { ES8323_DACCONTROL9, 0xf7 },
+ { ES8323_DACCONTROL10, 0xfd },
+ { ES8323_DACCONTROL11, 0xff },
+ { ES8323_DACCONTROL12, 0x1f },
+ { ES8323_DACCONTROL13, 0xf7 },
+ { ES8323_DACCONTROL14, 0xfd },
+ { ES8323_DACCONTROL15, 0xff },
+ { ES8323_DACCONTROL16, 0x00 },
+ { ES8323_DACCONTROL17, 0x38 },
+ { ES8323_DACCONTROL18, 0x38 },
+ { ES8323_DACCONTROL19, 0x38 },
+ { ES8323_DACCONTROL20, 0x38 },
+ { ES8323_DACCONTROL21, 0x38 },
+ { ES8323_DACCONTROL22, 0x38 },
+ { ES8323_DACCONTROL23, 0x00 },
+ { ES8323_LOUT1_VOL, 0x00 },
+ { ES8323_ROUT1_VOL, 0x00 },
+};
+
+static const char *const es8323_stereo_3d_texts[] = { "No 3D ", "Level 1", "Level 2", "Level 3",
+ "Level 4", "Level 5", "Level 6", "Level 7" };
+static SOC_ENUM_SINGLE_DECL(es8323_stereo_3d_enum, ES8323_DACCONTROL7, 2, es8323_stereo_3d_texts);
+
+static const char *const es8323_alc_func_texts[] = { "Off", "Right", "Left", "Stereo" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_function_enum,
+ ES8323_ADCCONTROL10, 6, es8323_alc_func_texts);
+
+static const char *const es8323_ng_type_texts[] = { "Constant PGA Gain", "Mute ADC Output" };
+static SOC_ENUM_SINGLE_DECL(es8323_alc_ng_type_enum, ES8323_ADCCONTROL14, 1, es8323_ng_type_texts);
+
+static const char *const es8323_deemph_texts[] = { "None", "32Khz", "44.1Khz", "48Khz" };
+static SOC_ENUM_SINGLE_DECL(es8323_playback_deemphasis_enum,
+ ES8323_DACCONTROL6, 6, es8323_deemph_texts);
+
+static const char *const es8323_adcpol_texts[] = { "Normal", "L Invert",
+ "R Invert", "L + R Invert" };
+static SOC_ENUM_SINGLE_DECL(es8323_capture_polarity_enum,
+ ES8323_ADCCONTROL6, 6, es8323_adcpol_texts);
+
+static const DECLARE_TLV_DB_SCALE(es8323_adc_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_dac_tlv, -9600, 50, 1);
+static const DECLARE_TLV_DB_SCALE(es8323_out_tlv, -4500, 150, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(es8323_bypass_tlv2, -15, 300, 0);
+
+static const struct snd_kcontrol_new es8323_snd_controls[] = {
+ SOC_ENUM("3D Mode", es8323_stereo_3d_enum),
+ SOC_ENUM("ALC Capture Function", es8323_alc_function_enum),
+ SOC_ENUM("ALC Capture NG Type", es8323_alc_ng_type_enum),
+ SOC_ENUM("Playback De-emphasis", es8323_playback_deemphasis_enum),
+ SOC_ENUM("Capture Polarity", es8323_capture_polarity_enum),
+ SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
+ SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
+ SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
+ SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
+ SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
+ SOC_SINGLE("Capture Mute Switch", ES8323_ADC_MUTE, 2, 1, 0),
+ SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
+ 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
+ 8, 0, es8323_bypass_tlv),
+ SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
+ 7, 1, es8323_bypass_tlv2),
+ SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
+ 3, 7, 1, es8323_bypass_tlv2),
+ SOC_DOUBLE_R_TLV("PCM Volume", ES8323_LDAC_VOL, ES8323_RDAC_VOL,
+ 0, 192, 1, es8323_dac_tlv),
+ SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_LADC_VOL,
+ ES8323_RADC_VOL, 0, 192, 1, es8323_adc_tlv),
+ SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_LOUT1_VOL,
+ ES8323_ROUT1_VOL, 0, 33, 0, es8323_out_tlv),
+ SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_LOUT2_VOL,
+ ES8323_ROUT2_VOL, 0, 33, 0, es8323_out_tlv),
+};
+
+/* Left DAC Route */
+static const char *const es8323_pga_sell[] = { "Line 1L", "Line 2L", "NC", "DifferentialL" };
+static SOC_ENUM_SINGLE_DECL(es8323_left_dac_enum, ES8323_ADCCONTROL2, 6, es8323_pga_sell);
+static const struct snd_kcontrol_new es8323_left_dac_mux_controls =
+ SOC_DAPM_ENUM("Left DAC Route", es8323_left_dac_enum);
+
+/* Right DAC Route */
+static const char *const es8323_pga_selr[] = { "Line 1R", "Line 2R", "NC", "DifferentialR" };
+static SOC_ENUM_SINGLE_DECL(es8323_right_dac_enum, ES8323_ADCCONTROL2, 4, es8323_pga_selr);
+static const struct snd_kcontrol_new es8323_right_dac_mux_controls =
+ SOC_DAPM_ENUM("Right DAC Route", es8323_right_dac_enum);
+
+/* Left Line Mux */
+static const char *const es8323_lin_sell[] = { "Line 1L", "Line 2L", "NC", "MicL" };
+static SOC_ENUM_SINGLE_DECL(es8323_llin_enum, ES8323_DACCONTROL16, 3, es8323_lin_sell);
+static const struct snd_kcontrol_new es8323_left_line_controls =
+ SOC_DAPM_ENUM("LLIN Mux", es8323_llin_enum);
+
+/* Right Line Mux */
+static const char *const es8323_lin_selr[] = { "Line 1R", "Line 2R", "NC", "MicR" };
+static SOC_ENUM_SINGLE_DECL(es8323_rlin_enum, ES8323_DACCONTROL16, 0, es8323_lin_selr);
+static const struct snd_kcontrol_new es8323_right_line_controls =
+ SOC_DAPM_ENUM("RLIN Mux", es8323_rlin_enum);
+
+/* Differential Mux */
+static const char *const es8323_diffmux_sel[] = { "Line 1", "Line 2" };
+static SOC_ENUM_SINGLE_DECL(es8323_diffmux_enum, ES8323_ADCCONTROL3, 7, es8323_diffmux_sel);
+static const struct snd_kcontrol_new es8323_diffmux_controls =
+ SOC_DAPM_ENUM("Route2", es8323_diffmux_enum);
+
+/* Mono ADC Mux */
+static const char *const es8323_mono_adc_mux[] = { "Stereo", "Mono (Left)", "Mono (Right)" };
+static SOC_ENUM_SINGLE_DECL(es8323_mono_adc_mux_enum, ES8323_ADCCONTROL3, 3, es8323_mono_adc_mux);
+static const struct snd_kcontrol_new es8323_mono_adc_mux_controls =
+ SOC_DAPM_ENUM("Mono Mux", es8323_mono_adc_mux_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new es8323_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Left Playback Switch", ES8323_DACCONTROL17, 7, 1, 0),
+ SOC_DAPM_SINGLE("Left Bypass Switch", ES8323_DACCONTROL17, 6, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new es8323_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("Right Playback Switch", ES8323_DACCONTROL20, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right Bypass Switch", ES8323_DACCONTROL20, 6, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("LINPUT1"),
+ SND_SOC_DAPM_INPUT("LINPUT2"),
+ SND_SOC_DAPM_INPUT("RINPUT1"),
+ SND_SOC_DAPM_INPUT("RINPUT2"),
+
+ SND_SOC_DAPM_MICBIAS("Mic Bias", SND_SOC_NOPM, 3, 1),
+
+ /* Muxes */
+ SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_left_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0, &es8323_right_dac_mux_controls),
+ SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0, &es8323_diffmux_controls),
+ SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0, &es8323_mono_adc_mux_controls),
+ SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0, &es8323_left_line_controls),
+ SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0, &es8323_right_line_controls),
+
+ SND_SOC_DAPM_ADC("Right ADC", "Right Capture", SND_SOC_NOPM, 4, 1),
+ SND_SOC_DAPM_ADC("Left ADC", "Left Capture", SND_SOC_NOPM, 5, 1),
+ SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),
+ SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),
+
+ SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_left_mixer_controls[0],
+ ARRAY_SIZE(es8323_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ &es8323_right_mixer_controls[0],
+ ARRAY_SIZE(es8323_right_mixer_controls)),
+
+ SND_SOC_DAPM_PGA("Right ADC Power", SND_SOC_NOPM, 6, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Left ADC Power", SND_SOC_NOPM, 7, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 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_OUTPUT("VREF"),
+};
+
+static const struct snd_soc_dapm_route es8323_dapm_routes[] = {
+ /*12.22*/
+ {"Left PGA Mux", "Line 1L", "LINPUT1"},
+ {"Left PGA Mux", "Line 2L", "LINPUT2"},
+ {"Left PGA Mux", "DifferentialL", "Differential Mux"},
+
+ {"Right PGA Mux", "Line 1R", "RINPUT1"},
+ {"Right PGA Mux", "Line 2R", "RINPUT2"},
+ {"Right PGA Mux", "DifferentialR", "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", "Right PGA Mux"},
+ {"Left ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+
+ {"Right ADC Mux", "Stereo", "Left PGA Mux"},
+ {"Right ADC Mux", "Stereo", "Right PGA Mux"},
+ {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+
+ {"Left ADC Power", NULL, "Left ADC Mux"},
+ {"Right ADC Power", NULL, "Right ADC Mux"},
+ {"Left ADC", NULL, "Left ADC Power"},
+ {"Right ADC", NULL, "Right ADC Power"},
+
+ {"Left Line Mux", "Line 1L", "LINPUT1"},
+ {"Left Line Mux", "Line 2L", "LINPUT2"},
+ {"Left Line Mux", "MicL", "Left PGA Mux"},
+
+ {"Right Line Mux", "Line 1R", "RINPUT1"},
+ {"Right Line Mux", "Line 2R", "RINPUT2"},
+ {"Right Line Mux", "MicR", "Right PGA Mux"},
+
+ {"Left Mixer", "Left Playback Switch", "Left DAC"},
+ {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+
+ {"Right Mixer", "Right 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:4;
+ u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct coeff_div es8323_coeff_div[] = {
+ /* 8k */
+ {12288000, 8000, 1536, 0xa, 0x0},
+ {11289600, 8000, 1408, 0x9, 0x0},
+ {18432000, 8000, 2304, 0xc, 0x0},
+ {16934400, 8000, 2112, 0xb, 0x0},
+ {12000000, 8000, 1500, 0xb, 0x1},
+
+ /* 11.025k */
+ {11289600, 11025, 1024, 0x7, 0x0},
+ {16934400, 11025, 1536, 0xa, 0x0},
+ {12000000, 11025, 1088, 0x9, 0x1},
+
+ /* 16k */
+ {12288000, 16000, 768, 0x6, 0x0},
+ {18432000, 16000, 1152, 0x8, 0x0},
+ {12000000, 16000, 750, 0x7, 0x1},
+
+ /* 22.05k */
+ {11289600, 22050, 512, 0x4, 0x0},
+ {16934400, 22050, 768, 0x6, 0x0},
+ {12000000, 22050, 544, 0x6, 0x1},
+
+ /* 32k */
+ {12288000, 32000, 384, 0x3, 0x0},
+ {18432000, 32000, 576, 0x5, 0x0},
+ {12000000, 32000, 375, 0x4, 0x1},
+
+ /* 44.1k */
+ {11289600, 44100, 256, 0x2, 0x0},
+ {16934400, 44100, 384, 0x3, 0x0},
+ {12000000, 44100, 272, 0x3, 0x1},
+
+ /* 48k */
+ {12288000, 48000, 256, 0x2, 0x0},
+ {18432000, 48000, 384, 0x3, 0x0},
+ {12000000, 48000, 250, 0x2, 0x1},
+
+ /* 88.2k */
+ {11289600, 88200, 128, 0x0, 0x0},
+ {16934400, 88200, 192, 0x1, 0x0},
+ {12000000, 88200, 136, 0x1, 0x1},
+
+ /* 96k */
+ {12288000, 96000, 128, 0x0, 0x0},
+ {18432000, 96000, 192, 0x1, 0x0},
+ {12000000, 96000, 125, 0x0, 0x1},
+};
+
+static unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12288 = {
+ .count = ARRAY_SIZE(rates_12288),
+ .list = rates_12288,
+};
+
+static unsigned int rates_112896[] = {
+ 8000, 11025, 22050, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_112896 = {
+ .count = ARRAY_SIZE(rates_112896),
+ .list = rates_112896,
+};
+
+static unsigned int rates_12[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000,
+ 32000, 44100, 48000, 48000, 88235, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12 = {
+ .count = ARRAY_SIZE(rates_12),
+ .list = rates_12,
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(es8323_coeff_div); i++) {
+ if (es8323_coeff_div[i].rate == rate &&
+ es8323_coeff_div[i].mclk == mclk)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int es8323_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 es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 11289600:
+ case 18432000:
+ case 22579200:
+ case 36864000:
+ es8323->sysclk_constraints = &constraints_112896;
+ break;
+ case 12288000:
+ case 16934400:
+ case 24576000:
+ case 33868800:
+ es8323->sysclk_constraints = &constraints_12288;
+ break;
+ case 12000000:
+ case 24000000:
+ es8323->sysclk_constraints = &constraints_12;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ es8323->sysclk = freq;
+ return 0;
+}
+
+static int es8323_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ u8 iface = snd_soc_component_read(component, ES8323_MASTERMODE);
+ u8 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE);
+ u8 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_BC_FP:
+ iface |= 0x80;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ iface &= 0x7f;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ adciface &= 0xfc;
+ daciface &= 0xf8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adciface &= 0xfd;
+ daciface &= 0xf9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adciface &= 0xfe;
+ daciface &= 0xfa;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ case SND_SOC_DAIFMT_DSP_B:
+ adciface &= 0xff;
+ daciface &= 0xfb;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xdf;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x20;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x20;
+ adciface &= 0xdf;
+ daciface &= 0xbf;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xdf;
+ adciface |= 0x20;
+ daciface |= 0x40;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, iface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+
+ return 0;
+}
+
+static int es8323_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ if (es8323->sysclk) {
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ es8323->sysclk_constraints);
+ }
+
+ return 0;
+}
+
+static int es8323_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 es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ u16 srate = snd_soc_component_read(component, ES8323_MASTERMODE) & 0x80;
+ u16 adciface = snd_soc_component_read(component, ES8323_ADC_IFACE) & 0xe3;
+ u16 daciface = snd_soc_component_read(component, ES8323_DAC_IFACE) & 0xc7;
+ int coeff;
+
+ coeff = get_coeff(es8323->sysclk, params_rate(params));
+ if (coeff < 0) {
+ coeff = get_coeff(es8323->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), es8323->sysclk);
+ return coeff;
+ }
+
+ /* bit size */
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adciface |= 0xc;
+ daciface |= 0x18;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adciface |= 0x4;
+ daciface |= 0x8;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adciface |= 0x10;
+ daciface |= 0x20;
+ break;
+ }
+
+ snd_soc_component_write(component, ES8323_DAC_IFACE, daciface);
+ snd_soc_component_write(component, ES8323_ADC_IFACE, adciface);
+
+ snd_soc_component_write(component, ES8323_MASTERMODE, srate);
+ snd_soc_component_write(component, ES8323_ADCCONTROL5,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+ snd_soc_component_write(component, ES8323_DACCONTROL2,
+ es8323_coeff_div[coeff].sr |
+ (es8323_coeff_div[coeff].usb) << 4);
+
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+
+ return 0;
+}
+
+static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ u32 val = mute ? 0x6 : 0x2;
+
+ snd_soc_component_write(component, ES8323_DAC_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es8323_ops = {
+ .startup = es8323_pcm_startup,
+ .hw_params = es8323_pcm_hw_params,
+ .set_fmt = es8323_set_dai_fmt,
+ .set_sysclk = es8323_set_dai_sysclk,
+ .mute_stream = es8323_mute_stream,
+};
+
+#define ES8323_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver es8323_dai = {
+ .name = "ES8323 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = ES8323_FORMATS,
+ },
+ .ops = &es8323_ops,
+ .symmetric_rate = 1,
+};
+
+static int es8323_probe(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es8323->component = component;
+
+ es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(es8323->mclk)) {
+ dev_err(component->dev, "unable to get mclk\n");
+ return PTR_ERR(es8323->mclk);
+ }
+
+ if (!es8323->mclk)
+ dev_warn(component->dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to enable mclk\n");
+ return ret;
+ }
+
+ snd_soc_component_write(component, ES8323_CONTROL2, 0x60);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+
+ return 0;
+}
+
+static int es8323_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8323->mclk);
+ if (ret)
+ return ret;
+
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xf0);
+ usleep_range(18000, 20000);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0x3c);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x09);
+ snd_soc_component_write(component, ES8323_ADCCONTROL14, 0x00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7c);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0x00);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0x00);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0x59);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8323->mclk);
+ snd_soc_component_write(component, ES8323_ADCPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_DACPOWER, 0xC0);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW1, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPLOPOW2, 0xff);
+ snd_soc_component_write(component, ES8323_CHIPPOWER, 0xff);
+ snd_soc_component_write(component, ES8323_ANAVOLMANAG, 0x7b);
+ break;
+ }
+
+ return 0;
+}
+
+static void es8323_remove(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ clk_disable_unprepare(es8323->mclk);
+ es8323_set_bias_level(component, SND_SOC_BIAS_OFF);
+}
+
+static int es8323_suspend(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, true);
+ regcache_mark_dirty(es8323->regmap);
+
+ return 0;
+}
+
+static int es8323_resume(struct snd_soc_component *component)
+{
+ struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(es8323->regmap, false);
+ regcache_sync(es8323->regmap);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_es8323 = {
+ .probe = es8323_probe,
+ .remove = es8323_remove,
+ .suspend = es8323_suspend,
+ .resume = es8323_resume,
+ .set_bias_level = es8323_set_bias_level,
+ .controls = es8323_snd_controls,
+ .num_controls = ARRAY_SIZE(es8323_snd_controls),
+ .dapm_widgets = es8323_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8323_dapm_widgets),
+ .dapm_routes = es8323_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8323_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config es8323_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+ .use_single_write = true,
+ .max_register = 0x53,
+ .reg_defaults = es8323_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(es8323_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int es8323_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8323_priv *es8323;
+ struct device *dev = &i2c_client->dev;
+
+ es8323 = devm_kzalloc(dev, sizeof(*es8323), GFP_KERNEL);
+ if (!es8323)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8323);
+
+ es8323->regmap = devm_regmap_init_i2c(i2c_client, &es8323_regmap);
+ if (IS_ERR(es8323->regmap))
+ return PTR_ERR(es8323->regmap);
+
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_es8323,
+ &es8323_dai, 1);
+}
+
+static const struct i2c_device_id es8323_i2c_id[] = {
+ { "es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
+
+static const struct acpi_device_id es8323_acpi_match[] = {
+ { "ESSX8323", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, es8323_acpi_match);
+
+static const struct of_device_id es8323_of_match[] = {
+ { .compatible = "everest,es8323" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8323_of_match);
+
+static struct i2c_driver es8323_i2c_driver = {
+ .driver = {
+ .name = "ES8323",
+ .acpi_match_table = es8323_acpi_match,
+ .of_match_table = es8323_of_match,
+ },
+ .probe = es8323_i2c_probe,
+ .id_table = es8323_i2c_id,
+};
+module_i2c_driver(es8323_i2c_driver);
+
+MODULE_DESCRIPTION("Everest Semi ES8323 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8323.h b/sound/soc/codecs/es8323.h
new file mode 100644
index 000000000000..f986c9301dc6
--- /dev/null
+++ b/sound/soc/codecs/es8323.h
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ * Binbin Zhou <zhoubinbin@loongson.cn>
+ *
+ */
+
+#ifndef _ES8323_H
+#define _ES8323_H
+
+/* ES8323 register space */
+
+/* Chip Control and Power Management */
+#define ES8323_CONTROL1 0x00
+#define ES8323_CONTROL2 0x01
+#define ES8323_CHIPPOWER 0x02
+#define ES8323_ADCPOWER 0x03
+#define ES8323_DACPOWER 0x04
+#define ES8323_CHIPLOPOW1 0x05
+#define ES8323_CHIPLOPOW2 0x06
+#define ES8323_ANAVOLMANAG 0x07
+#define ES8323_MASTERMODE 0x08
+
+/* ADC Control */
+#define ES8323_ADCCONTROL1 0x09
+#define ES8323_ADCCONTROL2 0x0a
+#define ES8323_ADCCONTROL3 0x0b
+#define ES8323_ADCCONTROL4 0x0c
+#define ES8323_ADCCONTROL5 0x0d
+#define ES8323_ADCCONTROL6 0x0e
+#define ES8323_ADC_MUTE 0x0f
+#define ES8323_LADC_VOL 0x10
+#define ES8323_RADC_VOL 0x11
+#define ES8323_ADCCONTROL10 0x12
+#define ES8323_ADCCONTROL11 0x13
+#define ES8323_ADCCONTROL12 0x14
+#define ES8323_ADCCONTROL13 0x15
+#define ES8323_ADCCONTROL14 0x16
+
+/* DAC Control */
+#define ES8323_DACCONTROL1 0x17
+#define ES8323_DACCONTROL2 0x18
+#define ES8323_DAC_MUTE 0x19
+#define ES8323_LDAC_VOL 0x1a
+#define ES8323_RDAC_VOL 0x1b
+#define ES8323_DACCONTROL6 0x1c
+#define ES8323_DACCONTROL7 0x1d
+#define ES8323_DACCONTROL8 0x1e
+#define ES8323_DACCONTROL9 0x1f
+#define ES8323_DACCONTROL10 0x20
+#define ES8323_DACCONTROL11 0x21
+#define ES8323_DACCONTROL12 0x22
+#define ES8323_DACCONTROL13 0x23
+#define ES8323_DACCONTROL14 0x24
+#define ES8323_DACCONTROL15 0x25
+#define ES8323_DACCONTROL16 0x26
+#define ES8323_DACCONTROL17 0x27
+#define ES8323_DACCONTROL18 0x28
+#define ES8323_DACCONTROL19 0x29
+#define ES8323_DACCONTROL20 0x2a
+#define ES8323_DACCONTROL21 0x2b
+#define ES8323_DACCONTROL22 0x2c
+#define ES8323_DACCONTROL23 0x2d
+#define ES8323_LOUT1_VOL 0x2e
+#define ES8323_ROUT1_VOL 0x2f
+#define ES8323_LOUT2_VOL 0x30
+#define ES8323_ROUT2_VOL 0x31
+#define ES8323_DACCONTROL28 0x32
+#define ES8323_DACCONTROL29 0x33
+#define ES8323_DACCONTROL30 0x34
+
+#define ES8323_ADC_IFACE ES8323_ADCCONTROL4
+#define ES8323_ADC_SRATE ES8323_ADCCONTROL5
+#define ES8323_DAC_IFACE ES8323_DACCONTROL1
+#define ES8323_DAC_SRATE ES8323_DACCONTROL2
+#endif
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
new file mode 100644
index 000000000000..78c4e68f6002
--- /dev/null
+++ b/sound/soc/codecs/es8326.c
@@ -0,0 +1,1374 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es8326.c -- es8326 ALSA SoC audio driver
+// Copyright Everest Semiconductor Co., Ltd
+//
+// Authors: David Yang <yangxiaohua@everest-semi.com>
+//
+
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#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 jack_pol;
+ u8 interrupt_src;
+ u8 interrupt_clk;
+ u8 hpl_vol;
+ u8 hpr_vol;
+ bool jd_inverted;
+ unsigned int sysclk;
+
+ bool calibrated;
+ int version;
+ int hp;
+ int jack_remove_retry;
+};
+
+static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x20;
+ crosstalk_l &= 0xf0;
+ crosstalk = crosstalk_h >> 1 | crosstalk_l >> 4;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk1_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = (crosstalk & 0x10) << 1;
+ crosstalk_l &= 0x0f;
+ crosstalk_l |= (crosstalk & 0x0f) << 4;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x20, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_crosstalk2_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ regmap_read(es8326->regmap, ES8326_DAC_RAMPRATE, &crosstalk_h);
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h &= 0x10;
+ crosstalk_l &= 0x0f;
+ crosstalk = crosstalk_h | crosstalk_l;
+ ucontrol->value.integer.value[0] = crosstalk;
+
+ return 0;
+}
+
+static int es8326_crosstalk2_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int crosstalk_h, crosstalk_l;
+ unsigned int crosstalk;
+
+ crosstalk = ucontrol->value.integer.value[0];
+ regmap_read(es8326->regmap, ES8326_DAC_CROSSTALK, &crosstalk_l);
+ crosstalk_h = crosstalk & 0x10;
+ crosstalk_l &= 0xf0;
+ crosstalk_l |= crosstalk & 0x0f;
+ regmap_update_bits(es8326->regmap, ES8326_DAC_RAMPRATE,
+ 0x10, crosstalk_h);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, crosstalk_l);
+
+ return 0;
+}
+
+static int es8326_hplvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpl_vol;
+
+ return 0;
+}
+
+static int es8326_hplvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpl_vol != hp_vol) {
+ es8326->hpl_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x70, (hp_vol << 4));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int es8326_hprvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpr_vol;
+
+ return 0;
+}
+
+static int es8326_hprvol_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int hp_vol;
+
+ hp_vol = ucontrol->value.integer.value[0];
+ if (hp_vol > 5)
+ return -EINVAL;
+ if (es8326->hpr_vol != hp_vol) {
+ es8326->hpr_vol = hp_vol;
+ if (hp_vol >= 3)
+ hp_vol++;
+ regmap_update_bits(es8326->regmap, ES8326_HP_VOL,
+ 0x07, hp_vol);
+ return 1;
+ }
+
+ return 0;
+}
+
+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 char *const hp_spkvol_switch[] = {
+ "HPVOL: HPL+HPL, SPKVOL: HPL+HPL",
+ "HPVOL: HPL+HPR, SPKVOL: HPL+HPR",
+ "HPVOL: HPL+HPL, SPKVOL: SPKL+SPKR",
+ "HPVOL: HPL+HPR, SPKVOL: SPKL+SPKR",
+};
+
+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 soc_enum hpvol_spkvol_switch =
+ SOC_ENUM_SINGLE(ES8326_HP_MISC, 6, 4, hp_spkvol_switch);
+
+static const struct snd_kcontrol_new es8326_snd_controls[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", ES8326_DACL_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),
+
+ SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk1_get, es8326_crosstalk1_set),
+ SOC_SINGLE_EXT("CROSSTALK2", SND_SOC_NOPM, 0, 31, 0,
+ es8326_crosstalk2_get, es8326_crosstalk2_set),
+ SOC_SINGLE_EXT("HPL Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hplvol_get, es8326_hplvol_set),
+ SOC_SINGLE_EXT("HPR Volume", SND_SOC_NOPM, 0, 5, 0,
+ es8326_hprvol_get, es8326_hprvol_set),
+
+ SOC_SINGLE_TLV("HPL Playback Volume", ES8326_DACL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("HPR Playback Volume", ES8326_DACR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKL Playback Volume", ES8326_SPKL_VOL, 0, 0xbf, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("SPKR Playback Volume", ES8326_SPKR_VOL, 0, 0xbf, 0, dac_vol_tlv),
+
+ SOC_ENUM("HPVol SPKVol Switch", hpvol_spkvol_switch),
+};
+
+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),
+
+ /* 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 L", NULL, "MIC1"},
+ {"ADC R", NULL, "MIC2"},
+ {"ADC L", NULL, "MIC3"},
+ {"ADC R", NULL, "MIC4"},
+
+ {"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 bool es8326_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_BIAS_SW1:
+ case ES8326_BIAS_SW2:
+ case ES8326_BIAS_SW3:
+ case ES8326_BIAS_SW4:
+ case ES8326_ADC_HPFS1:
+ case ES8326_ADC_HPFS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8326_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .use_single_read = true,
+ .use_single_write = true,
+ .volatile_reg = es8326_volatile_register,
+ .writeable_reg = es8326_writeable_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_v0[] = {
+ {64, 8000, 512000, 0x60, 0x01, 0x0F, 0x75, 0x0A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 8000, 1024000, 0x60, 0x00, 0x33, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x01, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {192, 32000, 6144000, 0xE0, 0x02, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 8000, 2048000, 0x60, 0x00, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x00, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {384, 32000, 12288000, 0xE0, 0x05, 0x03, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {400, 48000, 19200000, 0xE9, 0x04, 0x0F, 0x6d, 0x4A, 0x0A, 0x1F, 0x1F},
+
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x01, 0x03, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0x4A, 0x0A, 0x1F, 0x1F},
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x0A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x0A, 0x1B, 0x1F, 0x3F},
+};
+
+static const struct _coeff_div coeff_div_v3[] = {
+ {32, 8000, 256000, 0x60, 0x00, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {32, 16000, 512000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {32, 44100, 1411200, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {32, 48000, 1536000, 0x00, 0x00, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+ {36, 8000, 288000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {36, 16000, 576000, 0x20, 0x00, 0x0D, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {48, 8000, 384000, 0x60, 0x02, 0x1F, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {48, 16000, 768000, 0x20, 0x02, 0x0F, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {48, 48000, 2304000, 0x00, 0x02, 0x0D, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {64, 8000, 512000, 0x60, 0x00, 0x35, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {64, 16000, 1024000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {64, 44100, 2822400, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {64, 48000, 3072000, 0xE0, 0x00, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {72, 8000, 576000, 0x20, 0x00, 0x13, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {72, 16000, 1152000, 0x20, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x23, 0x47},
+ {96, 8000, 768000, 0x60, 0x02, 0x1D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {96, 16000, 1536000, 0x20, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x3F},
+ {100, 48000, 4800000, 0x04, 0x04, 0x3F, 0x6D, 0xB8, 0x08, 0x4f, 0x1f},
+ {125, 48000, 6000000, 0x04, 0x04, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+
+ {128, 8000, 1024000, 0x60, 0x00, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {128, 16000, 2048000, 0x20, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {128, 44100, 5644800, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {128, 48000, 6144000, 0xE0, 0x00, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {144, 8000, 1152000, 0x20, 0x00, 0x03, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {144, 16000, 2304000, 0x20, 0x00, 0x11, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {192, 8000, 1536000, 0x60, 0x02, 0x0D, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {192, 32000, 6144000, 0xE0, 0x02, 0x31, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {192, 16000, 3072000, 0x20, 0x02, 0x05, 0x75, 0xCA, 0x1B, 0x1F, 0x3F},
+
+ {200, 48000, 9600000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {250, 48000, 12000000, 0x04, 0x04, 0x0F, 0x2D, 0xCA, 0x0A, 0x27, 0x27},
+ {256, 8000, 2048000, 0x60, 0x00, 0x31, 0x35, 0x08, 0x19, 0x1F, 0x7F},
+ {256, 16000, 4096000, 0x20, 0x00, 0x01, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {256, 44100, 11289600, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {256, 48000, 12288000, 0xE0, 0x01, 0x01, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {288, 8000, 2304000, 0x20, 0x00, 0x01, 0x35, 0x8A, 0x1B, 0x23, 0x47},
+ {384, 8000, 3072000, 0x60, 0x02, 0x05, 0x75, 0x8A, 0x1B, 0x1F, 0x7F},
+ {384, 16000, 6144000, 0x20, 0x02, 0x03, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {384, 32000, 12288000, 0xE0, 0x02, 0x01, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {384, 48000, 18432000, 0x00, 0x02, 0x01, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {400, 48000, 19200000, 0xE4, 0x04, 0x35, 0x6d, 0xCA, 0x0A, 0x1F, 0x1F},
+ {500, 48000, 24000000, 0xF8, 0x04, 0x3F, 0x6D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {512, 8000, 4096000, 0x60, 0x00, 0x01, 0x08, 0x19, 0x1B, 0x1F, 0x7F},
+ {512, 16000, 8192000, 0x20, 0x00, 0x30, 0x35, 0x08, 0x19, 0x1F, 0x3F},
+ {512, 44100, 22579200, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {512, 48000, 24576000, 0xE0, 0x00, 0x00, 0x2D, 0x48, 0x08, 0x1F, 0x1F},
+ {768, 8000, 6144000, 0x60, 0x02, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {768, 16000, 12288000, 0x20, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {768, 32000, 24576000, 0xE0, 0x02, 0x30, 0x2D, 0xCA, 0x0A, 0x1F, 0x1F},
+ {800, 48000, 38400000, 0x00, 0x18, 0x13, 0x2D, 0x8A, 0x0A, 0x1F, 0x1F},
+
+ {1024, 8000, 8192000, 0x60, 0x00, 0x30, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1024, 16000, 16384000, 0x20, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1152, 16000, 18432000, 0x20, 0x08, 0x11, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1536, 8000, 12288000, 0x60, 0x02, 0x01, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {1536, 16000, 24576000, 0x20, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x3F},
+ {1625, 8000, 13000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {1625, 16000, 26000000, 0x0C, 0x18, 0x1F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+ {2048, 8000, 16384000, 0x60, 0x00, 0x00, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {2304, 8000, 18432000, 0x40, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x5F},
+ {3072, 8000, 24576000, 0x60, 0x02, 0x10, 0x35, 0x8A, 0x1B, 0x1F, 0x7F},
+ {3250, 8000, 26000000, 0x0C, 0x18, 0x0F, 0x2D, 0x8A, 0x0A, 0x27, 0x27},
+};
+
+static inline int get_coeff(int mclk, int rate, int array,
+ const struct _coeff_div *coeff_div)
+{
+ int i;
+
+ for (i = 0; i < array; 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;
+ const struct _coeff_div *coeff_div;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u8 srate = 0;
+ int coeff, array;
+
+ if (es8326->version == 0) {
+ coeff_div = coeff_div_v0;
+ array = ARRAY_SIZE(coeff_div_v0);
+ } else {
+ coeff_div = coeff_div_v3;
+ array = ARRAY_SIZE(coeff_div_v3);
+ }
+ coeff = get_coeff(es8326->sysclk, params_rate(params), array, coeff_div);
+ /* 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) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ES8326_MUTE);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF,
+ 0x30, 0x00);
+ } else {
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x0F);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ }
+ } 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_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x00);
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x01);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ usleep_range(1000, 5000);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x30, 0x30);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
+ ES8326_MUTE_MASK, ~(ES8326_MUTE));
+ } else {
+ msleep(300);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x70);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x00);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
+ 0x0F, 0x00);
+ }
+ }
+ 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_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x02);
+ usleep_range(5000, 10000);
+ 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_PGA_PDN, 0x40);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
+ regmap_update_bits(es8326->regmap, ES8326_RESET, 0x02, 0x00);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
+ }
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x10);
+ }
+ regmap_update_bits(es8326->regmap, ES8326_CLK_INV, 0xc0, 0xc0);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_MUX, 0x80, 0x80);
+ break;
+ case SND_SOC_BIAS_OFF:
+ clk_disable_unprepare(es8326->mclk);
+ 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 = 0,
+};
+
+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 */
+ if ((iface == 0x6f) && (es8326->version > ES8326_VERSION_B))
+ cur_button = SND_JACK_BTN_0;
+ else
+ 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;
+ }
+ es8326_disable_micbias(es8326->component);
+ }
+ 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) && (es8326->version < ES8326_VERSION_B)) {
+ 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_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
+ snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
+ /* mute adc when mic path switch */
+ 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);
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x0a);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x03);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ if ((es8326->jack_remove_retry == 0) && (es8326->version < ES8326_VERSION_B)) {
+ 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 400ms.
+ * Don't report jack status.
+ */
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x00);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x10, 0x10);
+ usleep_range(50000, 70000);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x1f);
+ regmap_update_bits(es8326->regmap, ES8326_HP_DRIVER_REF, 0x0f, 0x0d);
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(400));
+ es8326->hp = 1;
+ goto exit;
+ }
+ if (es8326->jack->status & SND_JACK_HEADSET) {
+ /* detect button */
+ dev_dbg(comp->dev, "button pressed\n");
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ es8326_enable_micbias(es8326->component);
+ 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_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;
+
+ if (!es8326->jack)
+ goto out;
+
+ 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(300));
+
+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, &reg);
+ 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, 0x03);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
+ regmap_write(es8326->regmap, ES8326_CLK_DAC_SEL, 0x08);
+ 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, 0x23);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
+ usleep_range(15000, 20000);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ usleep_range(15000, 20000);
+ 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, &reg);
+ 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 void es8326_init(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x3E);
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+ usleep_range(10000, 15000);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0xd9);
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xd8);
+ /* set headphone default type and detect pin */
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x83);
+ 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, 0x89);
+ 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, 0x3d);
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
+
+ /* enable headphone driver */
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0xc4);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x23);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0x33);
+ 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);
+ regmap_write(es8326->regmap, ES8326_DAC_CROSSTALK, 0xaa);
+ regmap_write(es8326->regmap, ES8326_DAC_RAMPRATE, 0x00);
+ /* 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);
+
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ /* select vdda as micbias source */
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x03);
+ /* 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_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)));
+ usleep_range(5000, 10000);
+ es8326_enable_micbias(es8326->component);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO,
+ es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ 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_ADC_MUTE, 0x0f);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_LRCK, 0xff);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ es8326_disable_micbias(es8326->component);
+ if (es8326->version > ES8326_VERSION_B) {
+ regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x73, 0x10);
+ regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
+ }
+
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE, ES8326_INT_SRC_PIN9);
+}
+
+static int es8326_resume(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8326->regmap, false);
+ regcache_cache_bypass(es8326->regmap, true);
+ regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, &reg);
+ regcache_cache_bypass(es8326->regmap, false);
+ /* reset internal clock state */
+ if (reg == 0x05)
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
+ else
+ es8326_init(component);
+
+ regcache_sync(es8326->regmap);
+
+ es8326_irq(es8326->irq, es8326);
+ 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_MUX, 0x2d);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x00);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_OFF);
+ regcache_cache_only(es8326->regmap, true);
+
+ /* 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);
+
+ regcache_mark_dirty(es8326->regmap);
+ 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,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 = 0x00;
+ }
+ dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+
+ es8326_init(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)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ es8326_disable_jack_detect(component);
+ es8326_set_bias_level(component, SND_SOC_BIAS_OFF);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+}
+
+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;
+ es8326->jack_remove_retry = 0;
+ es8326->hp = 0;
+ es8326->hpl_vol = 0x03;
+ es8326->hpr_vol = 0x03;
+ 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 void es8326_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct snd_soc_component *component;
+ struct es8326_priv *es8326;
+
+ es8326 = i2c_get_clientdata(i2c);
+ component = es8326->component;
+ dev_dbg(component->dev, "Enter into %s\n", __func__);
+ cancel_delayed_work_sync(&es8326->jack_detect_work);
+ cancel_delayed_work_sync(&es8326->button_press_work);
+
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
+
+}
+
+static void es8326_i2c_remove(struct i2c_client *i2c)
+{
+ es8326_i2c_shutdown(i2c);
+}
+
+static const struct i2c_device_id es8326_i2c_id[] = {
+ {"es8326" },
+ {}
+};
+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,
+ .shutdown = es8326_i2c_shutdown,
+ .remove = es8326_i2c_remove,
+ .id_table = es8326_i2c_id,
+};
+module_i2c_driver(es8326_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8326 driver");
+MODULE_AUTHOR("David Yang <yangxiaohua@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
new file mode 100644
index 000000000000..c3e52e7bdef5
--- /dev/null
+++ b/sound/soc/codecs/es8326.h
@@ -0,0 +1,200 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * es8326.h -- es8326 ALSA SoC audio driver
+ * Copyright Everest Semiconductor Co.,Ltd
+ *
+ * Authors: David Yang <yangxiaohua@everest-semi.com>
+ */
+
+#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_DACL_VOL 0x50
+#define ES8326_DRC_RECOVERY 0x53
+#define ES8326_DRC_WINSIZE 0x54
+#define ES8326_DAC_CROSSTALK 0x55
+#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_DACR_VOL 0xF4
+#define ES8326_SPKL_VOL 0xF5
+#define ES8326_SPKR_VOL 0xF6
+#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
index 2d05b5d3a6ce..56bfbe9261ce 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -1,13 +1,10 @@
+// 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 <xobs@kosagi.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -19,37 +16,31 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "es8328", 0 },
+ { "es8328" },
+ { "es8388" },
{ }
};
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,
- const struct i2c_device_id *id)
+static int es8328_i2c_probe(struct i2c_client *i2c)
{
return es8328_probe(&i2c->dev,
devm_regmap_init_i2c(i2c, &es8328_regmap_config));
}
-static int es8328_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
-
static struct i2c_driver es8328_i2c_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
- .probe = es8328_i2c_probe,
- .remove = es8328_i2c_remove,
+ .probe = es8328_i2c_probe,
.id_table = es8328_id,
};
diff --git a/sound/soc/codecs/es8328-spi.c b/sound/soc/codecs/es8328-spi.c
index 8fbd935e1c76..88e353ae52a1 100644
--- a/sound/soc/codecs/es8328-spi.c
+++ b/sound/soc/codecs/es8328-spi.c
@@ -1,13 +1,10 @@
+// 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 <xobs@kosagi.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -28,19 +25,12 @@ static int es8328_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &es8328_regmap_config));
}
-static int es8328_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static struct spi_driver es8328_spi_driver = {
.driver = {
.name = "es8328",
.of_match_table = es8328_of_match,
},
.probe = es8328_spi_probe,
- .remove = es8328_spi_remove,
};
module_spi_driver(es8328_spi_driver);
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index f27325155ace..76159c45e6b5 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -1,18 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* es8328.c -- ES8328 ALSA SoC Audio driver
*
* Copyright 2014 Sutajio Ko-Usagi PTE LTD
*
* Author: Sean Cross <xobs@kosagi.com>
- *
- * 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.
*/
#include <linux/clk.h>
#include <linux/delay.h>
-#include <linux/of_device.h>
#include <linux/module.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -26,18 +22,30 @@
#include <sound/tlv.h>
#include "es8328.h"
-#define ES8328_SYSCLK_RATE_1X 11289600
-#define ES8328_SYSCLK_RATE_2X 22579200
+static const unsigned int rates_12288[] = {
+ 8000, 12000, 16000, 24000, 32000, 48000, 96000,
+};
-/* Run the codec at 22.5792 or 11.2896 MHz to support these rates */
-static struct {
- int rate;
- u8 ratio;
-} mclk_ratios[] = {
- { 8000, 9 },
- {11025, 7 },
- {22050, 4 },
- {44100, 2 },
+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 */
@@ -57,16 +65,25 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"HPVDD",
};
-#define ES8328_RATES (SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_22050 | \
- SNDRV_PCM_RATE_11025)
-#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+#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];
};
@@ -81,15 +98,22 @@ static SOC_ENUM_SINGLE_DECL(adcpol,
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(pga_tlv, 0, 300, 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 int deemph_settings[] = { 0, 32000, 44100, 48000 };
+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_codec *codec)
+static int es8328_set_deemph(struct snd_soc_component *component)
{
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int val, i, best;
/*
@@ -97,51 +121,55 @@ static int es8328_set_deemph(struct snd_soc_codec *codec)
* rate.
*/
if (es8328->deemph) {
- best = 1;
- for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
- if (abs(deemph_settings[i] - es8328->playback_fs) <
- abs(deemph_settings[best] - es8328->playback_fs))
+ 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 = best << 1;
+ val = deemph_settings[best].val;
} else {
- val = 0;
+ val = ES8328_DACCONTROL6_DEEMPH_OFF;
}
- dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+ dev_dbg(component->dev, "Set deemphasis %d\n", val);
- return snd_soc_update_bits(codec, ES8328_DACCONTROL6, 0x6, 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = es8328->deemph;
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int deemph = ucontrol->value.enumerated.item[0];
+ 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;
- ret = es8328_set_deemph(codec);
+ if (es8328->deemph == deemph)
+ return 0;
+
+ ret = es8328_set_deemph(component);
if (ret < 0)
return ret;
es8328->deemph = deemph;
- return 0;
+ return 1;
}
@@ -201,22 +229,20 @@ static const struct soc_enum es8328_rline_enum =
ARRAY_SIZE(es8328_line_texts),
es8328_line_texts);
static const struct snd_kcontrol_new es8328_right_line_controls =
- SOC_DAPM_ENUM("Route", es8328_lline_enum);
+ 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, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL17, 7, 1, 0),
- SOC_DAPM_SINGLE("Right Playback Switch", ES8328_DACCONTROL18, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL18, 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, 8, 1, 0),
- SOC_DAPM_SINGLE("Left Bypass Switch", ES8328_DACCONTROL19, 7, 1, 0),
- SOC_DAPM_SINGLE("Playback Switch", ES8328_DACCONTROL20, 8, 1, 0),
- SOC_DAPM_SINGLE("Right Bypass Switch", ES8328_DACCONTROL20, 7, 1, 0),
+ 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("Right Bypass Switch", ES8328_DACCONTROL20, 6, 1, 0),
};
static const char * const es8328_pga_sel[] = {
@@ -308,10 +334,10 @@ static const struct snd_soc_dapm_widget es8328_dapm_widgets[] = {
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,
+ SND_SOC_DAPM_MIXER("Left Mixer", ES8328_DACCONTROL17, 7, 0,
&es8328_left_mixer_controls[0],
ARRAY_SIZE(es8328_left_mixer_controls)),
- SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+ SND_SOC_DAPM_MIXER("Right Mixer", ES8328_DACCONTROL20, 7, 0,
&es8328_right_mixer_controls[0],
ARRAY_SIZE(es8328_right_mixer_controls)),
@@ -390,19 +416,14 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "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", NULL, "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", NULL, "Right DAC" },
{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
{ "DAC DIG", NULL, "DAC STM" },
@@ -423,76 +444,185 @@ static const struct snd_soc_dapm_route es8328_dapm_routes[] = {
{ "ROUT2", NULL, "Right Out 2" },
};
-static int es8328_mute(struct snd_soc_dai *dai, int mute)
+static int es8328_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- return snd_soc_update_bits(dai->codec, ES8328_DACCONTROL3,
+ 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_codec *codec = dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int clk_rate;
+ struct snd_soc_component *component = dai->component;
+ struct es8328_priv *es8328 = snd_soc_component_get_drvdata(component);
int i;
int reg;
- u8 ratio;
+ int wl;
+ int ratio;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2;
else
reg = ES8328_ADCCONTROL5;
- clk_rate = clk_get_rate(es8328->clk);
+ if (es8328->provider) {
+ if (!es8328->sysclk_constraints) {
+ dev_err(component->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
- if ((clk_rate != ES8328_SYSCLK_RATE_1X) &&
- (clk_rate != ES8328_SYSCLK_RATE_2X)) {
- dev_err(codec->dev,
- "%s: clock is running at %d Hz, not %d or %d Hz\n",
- __func__, clk_rate,
- ES8328_SYSCLK_RATE_1X, ES8328_SYSCLK_RATE_2X);
- 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;
}
- /* find master mode MCLK to sampling frequency ratio */
- ratio = mclk_ratios[0].rate;
- for (i = 1; i < ARRAY_SIZE(mclk_ratios); i++)
- if (params_rate(params) <= mclk_ratios[i].rate)
- ratio = mclk_ratios[i].ratio;
+ 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(codec);
+ 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;
+ unsigned int round_freq;
+
+ /*
+ * Allow a small tolerance for frequencies within 100hz. Note
+ * this value is chosen arbitrarily.
+ */
+ round_freq = DIV_ROUND_CLOSEST(freq, 100) * 100;
+
+ switch (round_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;
}
- return snd_soc_update_bits(codec, reg, ES8328_RATEMASK, ratio);
+ es8328->mclkdiv2 = mclkdiv2;
+ return 0;
}
static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- int clk_rate;
- u8 mode = ES8328_DACCONTROL1_DACWL_16;
-
- /* set master/slave audio interface */
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBM_CFM)
+ 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:
- mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_I2S;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_I2S;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_RJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_RJUST;
break;
case SND_SOC_DAIFMT_LEFT_J:
- mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ dac_mode |= ES8328_DACCONTROL1_DACFORMAT_LJUST;
+ adc_mode |= ES8328_ADCCONTROL4_ADCFORMAT_LJUST;
break;
default:
return -EINVAL;
@@ -502,23 +632,15 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF)
return -EINVAL;
- snd_soc_write(codec, ES8328_DACCONTROL1, mode);
- snd_soc_write(codec, ES8328_ADCCONTROL4, mode);
-
- /* Master serial port mode, with BCLK generated automatically */
- clk_rate = clk_get_rate(es8328->clk);
- if (clk_rate == ES8328_SYSCLK_RATE_1X)
- snd_soc_write(codec, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MSC);
- else
- snd_soc_write(codec, ES8328_MASTERMODE,
- ES8328_MASTERMODE_MCLKDIV2 |
- ES8328_MASTERMODE_MSC);
+ 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_codec *codec,
+static int es8328_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -527,8 +649,8 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
/* VREF, VMID=2x50k, digital enabled */
- snd_soc_write(codec, ES8328_CHIPPOWER, 0);
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ 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 |
@@ -536,8 +658,8 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ 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 |
@@ -547,12 +669,12 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
msleep(100);
}
- snd_soc_write(codec, ES8328_CONTROL2,
+ snd_soc_component_write(component, ES8328_CONTROL2,
ES8328_CONTROL2_OVERCURRENT_ON |
ES8328_CONTROL2_THERMAL_SHUTDOWN_ON);
/* VREF, VMID=2*500k, digital stopped */
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
ES8328_CONTROL1_VMIDSEL_500k |
@@ -560,20 +682,22 @@ static int es8328_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ES8328_CONTROL1,
+ snd_soc_component_update_bits(component, ES8328_CONTROL1,
ES8328_CONTROL1_VMIDSEL_MASK |
ES8328_CONTROL1_ENREF,
0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops es8328_dai_ops = {
+ .startup = es8328_startup,
.hw_params = es8328_hw_params,
- .digital_mute = es8328_mute,
+ .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 = {
@@ -593,82 +717,83 @@ static struct snd_soc_dai_driver es8328_dai = {
.formats = ES8328_FORMATS,
},
.ops = &es8328_dai_ops,
+ .symmetric_rate = 1,
};
-static int es8328_suspend(struct snd_soc_codec *codec)
+static int es8328_suspend(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "unable to disable regulators\n");
+ dev_err(component->dev, "unable to disable regulators\n");
return ret;
}
return 0;
}
-static int es8328_resume(struct snd_soc_codec *codec)
+static int es8328_resume(struct snd_soc_component *component)
{
- struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = clk_prepare_enable(es8328->clk);
if (ret) {
- dev_err(codec->dev, "unable to enable clock\n");
+ 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(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
regcache_mark_dirty(regmap);
ret = regcache_sync(regmap);
if (ret) {
- dev_err(codec->dev, "unable to sync regcache\n");
+ dev_err(component->dev, "unable to sync regcache\n");
return ret;
}
return 0;
}
-static int es8328_codec_probe(struct snd_soc_codec *codec)
+static int es8328_component_probe(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
int ret;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
ret = regulator_bulk_enable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
if (ret) {
- dev_err(codec->dev, "unable to enable regulators\n");
+ dev_err(component->dev, "unable to enable regulators\n");
return ret;
}
/* Setup clocks */
- es8328->clk = devm_clk_get(codec->dev, NULL);
+ es8328->clk = devm_clk_get(component->dev, NULL);
if (IS_ERR(es8328->clk)) {
- dev_err(codec->dev, "codec clock missing or invalid\n");
+ 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(codec->dev, "unable to prepare codec clk\n");
+ dev_err(component->dev, "unable to prepare codec clk\n");
goto clk_fail;
}
@@ -680,43 +805,44 @@ clk_fail:
return ret;
}
-static int es8328_remove(struct snd_soc_codec *codec)
+static void es8328_remove(struct snd_soc_component *component)
{
struct es8328_priv *es8328;
- es8328 = snd_soc_codec_get_drvdata(codec);
+ es8328 = snd_soc_component_get_drvdata(component);
- if (es8328->clk)
- clk_disable_unprepare(es8328->clk);
+ clk_disable_unprepare(es8328->clk);
regulator_bulk_disable(ARRAY_SIZE(es8328->supplies),
es8328->supplies);
-
- return 0;
}
const struct regmap_config es8328_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = ES8328_REG_MAX,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
EXPORT_SYMBOL_GPL(es8328_regmap_config);
-static struct snd_soc_codec_driver es8328_codec_driver = {
- .probe = es8328_codec_probe,
- .suspend = es8328_suspend,
- .resume = es8328_resume,
- .remove = es8328_remove,
- .set_bias_level = es8328_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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)
@@ -746,8 +872,8 @@ int es8328_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, es8328);
- return snd_soc_register_codec(dev,
- &es8328_codec_driver, &es8328_dai, 1);
+ return devm_snd_soc_register_component(dev,
+ &es8328_component_driver, &es8328_dai, 1);
}
EXPORT_SYMBOL_GPL(es8328_probe);
diff --git a/sound/soc/codecs/es8328.h b/sound/soc/codecs/es8328.h
index cb36afe10c0e..9109f6b5b045 100644
--- a/sound/soc/codecs/es8328.h
+++ b/sound/soc/codecs/es8328.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* es8328.h -- ES8328 ALSA SoC Audio driver
*/
@@ -22,7 +23,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#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 (7 << 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)
@@ -84,7 +85,20 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#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)
@@ -109,15 +123,13 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#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_24 (0 << 3)
-#define ES8328_DACCONTROL1_DACWL_20 (1 << 3)
-#define ES8328_DACCONTROL1_DACWL_18 (2 << 3)
-#define ES8328_DACCONTROL1_DACWL_16 (3 << 3)
-#define ES8328_DACCONTROL1_DACWL_32 (4 << 3)
+#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)
@@ -153,6 +165,7 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#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)
@@ -266,43 +279,6 @@ int es8328_probe(struct device *dev, struct regmap *regmap);
#define ES8328_REG_MAX 0x35
-#define ES8328_PLL1 0
-#define ES8328_PLL2 1
-
-/* clock inputs */
-#define ES8328_MCLK 0
-#define ES8328_PCMCLK 1
-
-/* clock divider id's */
-#define ES8328_PCMDIV 0
-#define ES8328_BCLKDIV 1
-#define ES8328_VXCLKDIV 2
-
-/* PCM clock dividers */
-#define ES8328_PCM_DIV_1 (0 << 6)
-#define ES8328_PCM_DIV_3 (2 << 6)
-#define ES8328_PCM_DIV_5_5 (3 << 6)
-#define ES8328_PCM_DIV_2 (4 << 6)
-#define ES8328_PCM_DIV_4 (5 << 6)
-#define ES8328_PCM_DIV_6 (6 << 6)
-#define ES8328_PCM_DIV_8 (7 << 6)
-
-/* BCLK clock dividers */
-#define ES8328_BCLK_DIV_1 (0 << 7)
-#define ES8328_BCLK_DIV_2 (1 << 7)
-#define ES8328_BCLK_DIV_4 (2 << 7)
-#define ES8328_BCLK_DIV_8 (3 << 7)
-
-/* VXCLK clock dividers */
-#define ES8328_VXCLK_DIV_1 (0 << 6)
-#define ES8328_VXCLK_DIV_2 (1 << 6)
-#define ES8328_VXCLK_DIV_4 (2 << 6)
-#define ES8328_VXCLK_DIV_8 (3 << 6)
-#define ES8328_VXCLK_DIV_16 (4 << 6)
-
-#define ES8328_DAI_HIFI 0
-#define ES8328_DAI_VOICE 1
-
#define ES8328_1536FS 1536
#define ES8328_1024FS 1024
#define ES8328_768FS 768
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
new file mode 100644
index 000000000000..36b0ebdce514
--- /dev/null
+++ b/sound/soc/codecs/es8375.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8375.c -- ES8375 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <linux/acpi.h>
+#include "es8375.h"
+
+struct es8375_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
+ unsigned int mclk_freq;
+ int mastermode;
+ u8 mclk_src;
+ u8 vddd;
+ enum snd_soc_bias_level bias_level;
+};
+
+static const char * const es8375_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
+static const DECLARE_TLV_DB_SCALE(es8375_adc_osr_gain_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_automute_attn_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_dmic_volume_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_vppscale_tlv, -388, 12, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_automute_attn_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_automute_ng_tlv, -9600, 600, 0);
+
+static const char *const es8375_ramprate_txt[] = {
+ "0.125dB/LRCK",
+ "0.125dB/2LRCK",
+ "0.125dB/4LRCK",
+ "0.125dB/8LRCK",
+ "0.125dB/16LRCK",
+ "0.125dB/32LRCK",
+ "0.125dB/64LRCK",
+ "0.125dB/128LRCK",
+ "disable softramp",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_ramprate, ES8375_ADC2,
+ ADC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_ramprate, ES8375_DAC2,
+ DAC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+
+static const char *const es8375_automute_ws_txt[] = {
+ "256 samples",
+ "512 samples",
+ "1024 samples",
+ "2048 samples",
+ "4096 samples",
+ "8192 samples",
+ "16384 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_automute_ws, ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_WS_SHIFT_3, es8375_automute_ws_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_automute_ws, ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_WS_SHIFT_5, es8375_automute_ws_txt);
+
+static const char *const es8375_dmic_pol_txt[] = {
+ "Low",
+ "High",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_dmic_pol, ES8375_ADC1,
+ DMIC_POL_SHIFT_4, es8375_dmic_pol_txt);
+
+static const char *const es8375_adc_hpf_txt[] = {
+ "Freeze Offset",
+ "Dynamic HPF",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_adc_hpf, ES8375_HPF1,
+ ADC_HPF_SHIFT_5, es8375_adc_hpf_txt);
+
+static const char *const es8375_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+static const struct soc_enum es8375_dmic_mux_enum =
+ SOC_ENUM_SINGLE(ES8375_ADC1, ADC_SRC_SHIFT_7,
+ ARRAY_SIZE(es8375_dmic_mux_txt), es8375_dmic_mux_txt);
+
+static const struct snd_kcontrol_new es8375_dmic_mux_controls =
+ SOC_DAPM_ENUM("ADC MUX", es8375_dmic_mux_enum);
+
+static const struct snd_kcontrol_new es8375_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8375_ADC_OSR_GAIN,
+ ADC_OSR_GAIN_SHIFT_0, ES8375_ADC_OSR_GAIN_MAX, 0,
+ es8375_adc_osr_gain_tlv),
+ SOC_SINGLE("ADC Invert Switch", ES8375_ADC1, ADC_INV_SHIFT_6, 1, 0),
+ SOC_SINGLE("ADC RAM Clear", ES8375_ADC1, ADC_RAMCLR_SHIFT_5, 1, 0),
+ SOC_ENUM("DMIC Polarity", es8375_dmic_pol),
+ SOC_SINGLE_TLV("DMIC Volume", ES8375_ADC1,
+ DMIC_GAIN_SHIFT_2, ES8375_DMIC_GAIN_MAX,
+ 0, es8375_adc_dmic_volume_tlv),
+ SOC_ENUM("ADC Ramp Rate", es8375_adc_ramprate),
+ SOC_SINGLE_TLV("ADC Volume", ES8375_ADC_VOLUME,
+ ADC_VOLUME_SHIFT_0, ES8375_ADC_VOLUME_MAX,
+ 0, es8375_adc_volume_tlv),
+ SOC_SINGLE("ADC Automute Switch", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_SHIFT_7, 1, 0),
+ SOC_ENUM("ADC Automute Winsize", es8375_adc_automute_ws),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Volume", ES8375_ADC_AUTOMUTE_ATTN,
+ ADC_AUTOMUTE_ATTN_SHIFT_0, ES8375_ADC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_adc_automute_attn_tlv),
+ SOC_ENUM("ADC HPF", es8375_adc_hpf),
+
+ SOC_SINGLE("DAC DSM Mute Switch", ES8375_DAC1, DAC_DSMMUTE_SHIFT_7, 1, 0),
+ SOC_SINGLE("DAC DEM Mute Switch", ES8375_DAC1, DAC_DEMMUTE_SHIFT_6, 1, 0),
+ SOC_SINGLE("DAC Invert Switch", ES8375_DAC1, DAC_INV_SHIFT_5, 1, 0),
+ SOC_SINGLE("DAC RAM Clear", ES8375_DAC1, DAC_RAMCLR_SHIFT_4, 1, 0),
+ SOC_ENUM("DAC Ramp Rate", es8375_dac_ramprate),
+ SOC_SINGLE_TLV("DAC Volume", ES8375_DAC_VOLUME,
+ DAC_VOLUME_SHIFT_0, ES8375_DAC_VOLUME_MAX,
+ 0, es8375_dac_volume_tlv),
+ SOC_SINGLE_TLV("DAC VPP Scale", ES8375_DAC_VPPSCALE,
+ DAC_VPPSCALE_SHIFT_0, ES8375_DAC_VPPSCALE_MAX,
+ 0, es8375_dac_vppscale_tlv),
+ SOC_SINGLE("DAC Automute Switch", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_EN_SHIFT_7, 1, 0),
+ SOC_SINGLE_TLV("DAC Automute Noise Gate", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_ENUM("DAC Automute Winsize", es8375_dac_automute_ws),
+ SOC_SINGLE_TLV("DAC Automute Volume", ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_ATTN_SHIFT_0, ES8375_DAC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_dac_automute_attn_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8375_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8375_SDP2,
+ ES8375_ADC_P2S_MUTE_SHIFT_5, 1),
+
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8375_dmic_mux_controls),
+
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8375_SDP,
+ SND_SOC_NOPM, 0),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8375_dapm_routes[] = {
+ {"ADC MUX", "AMIC", "MIC1"},
+ {"ADC MUX", "DMIC", "DMIC"},
+ {"PGA", NULL, "ADC MUX"},
+ {"Mono ADC", NULL, "PGA"},
+ {"AIF1TX", NULL, "Mono ADC"},
+
+ {"Mono DAC", NULL, "AIF1RX"},
+ {"OUT", NULL, "Mono DAC"},
+};
+
+struct _coeff_div {
+ u16 mclk_lrck_ratio;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0B;
+ u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
+};
+
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
+ {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
+ {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2},
+ {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2},
+ {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2},
+ {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2},
+ {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2},
+ {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2},
+ {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2},
+ {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2},
+ {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2},
+ {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2},
+ {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2},
+};
+
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
+{
+ int i;
+ u8 dmic_det, vddd_det;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int es8375_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 es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int par_width = params_width(params);
+ u8 dmic_enable, iface = 0;
+ unsigned int regv;
+ int coeff, ret;
+
+ if (es8375->mclk_src == ES8375_BCLK_PIN) {
+ regmap_update_bits(es8375->regmap,
+ ES8375_MCLK_SEL, 0x80, 0x80);
+
+ es8375->mclk_freq = 2 * (unsigned int)par_width * params_rate(params);
+ }
+
+ regmap_read(es8375->regmap, ES8375_ADC1, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8375->core_supply[ES8375_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8375->vddd = ES8375_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8375->vddd = ES8375_3V3;
+ break;
+ default:
+ es8375->vddd = ES8375_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8375->vddd, dmic_enable, es8375->mclk_freq, params_rate(params));
+ if (coeff < 0) {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ return coeff;
+ }
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4,
+ coeff_div[coeff].Reg0x04);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5,
+ coeff_div[coeff].Reg0x05);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6,
+ coeff_div[coeff].Reg0x06);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7,
+ coeff_div[coeff].Reg0x07);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8,
+ coeff_div[coeff].Reg0x08);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9,
+ coeff_div[coeff].Reg0x09);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10,
+ coeff_div[coeff].Reg0x0A);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR11,
+ coeff_div[coeff].Reg0x0B);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN,
+ coeff_div[coeff].Reg0x19);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iface |= 0x0c;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x10;
+ break;
+ }
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x1c, iface);
+
+ return 0;
+}
+
+static int es8375_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mclk_freq = freq;
+
+ return 0;
+}
+
+static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int iface, codeciface;
+
+ regmap_read(es8375->regmap, ES8375_SDP, &codeciface);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ es8375->mastermode = 1;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8375->mastermode = 0;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codeciface &= 0xFC;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ codeciface &= 0xFC;
+ codeciface |= 0x01;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ codeciface &= 0xDC;
+ codeciface |= 0x03;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ codeciface &= 0xDC;
+ codeciface |= 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_read(es8375->regmap, ES8375_CLK_MGR3, &iface);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xFE;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x01;
+ codeciface |= 0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x01;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xFE;
+ codeciface |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, iface);
+ regmap_write(es8375->regmap, ES8375_SDP, codeciface);
+
+ return 0;
+}
+
+static int es8375_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+ regmap_write(es8375->regmap, ES8375_CSM1, 0xA6);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ clk_disable_unprepare(es8375->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x00);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x00);
+ }
+
+ return 0;
+}
+
+#define es8375_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8375_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 es8375_ops = {
+ .hw_params = es8375_hw_params,
+ .mute_stream = es8375_mute,
+ .set_sysclk = es8375_set_sysclk,
+ .set_fmt = es8375_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8375_dai = {
+ .name = "ES8375 HiFi",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .ops = &es8375_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8375_init(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x95);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_DIV_SPKCLK, 0x18);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x82);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20);
+ regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0);
+ regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE);
+ regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ msleep(80);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x86);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6, 0x31);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7, 0x11);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8, 0x1F);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9, 0x00);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, 0x1F);
+ regmap_write(es8375->regmap, ES8375_ADC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7);
+ regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0);
+ regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE);
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+}
+
+static int es8375_suspend(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ regcache_cache_only(es8375->regmap, true);
+ regcache_mark_dirty(es8375->regmap);
+ return 0;
+}
+
+static int es8375_resume(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8375->regmap, false);
+ regcache_cache_bypass(es8375->regmap, true);
+ regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
+ regcache_cache_bypass(es8375->regmap, false);
+
+ if (reg == 0x00)
+ es8375_init(component);
+ else
+ es8375_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8375->regmap);
+
+ return 0;
+}
+
+static int es8375_codec_probe(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mastermode = 0;
+
+ es8375_init(component);
+
+ return 0;
+}
+
+static bool es8375_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8375_CHIP_VERSION:
+ case ES8375_CHIP_ID0:
+ case ES8375_CHIP_ID1:
+ case ES8375_SPK_OFFSET:
+ case ES8375_FLAGS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8375_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8375_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .writeable_reg = es8375_writeable_register,
+};
+
+static struct snd_soc_component_driver es8375_codec_driver = {
+ .probe = es8375_codec_probe,
+ .suspend = es8375_suspend,
+ .resume = es8375_resume,
+ .set_bias_level = es8375_set_bias_level,
+ .controls = es8375_snd_controls,
+ .num_controls = ARRAY_SIZE(es8375_snd_controls),
+ .dapm_widgets = es8375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8375_dapm_widgets),
+ .dapm_routes = es8375_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8375_dapm_routes),
+
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+};
+
+static int es8375_read_device_properities(struct device *dev, struct es8375_priv *es8375)
+{
+ int ret, i;
+
+ ret = device_property_read_u8(dev, "everest,mclk-src", &es8375->mclk_src);
+ if (ret != 0)
+ es8375->mclk_src = ES8375_MCLK_SOURCE;
+ dev_dbg(dev, "mclk-src %x", es8375->mclk_src);
+
+ for (i = 0; i < ARRAY_SIZE(es8375_core_supplies); i++)
+ es8375->core_supply[i].supply = es8375_core_supplies[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ es8375->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(es8375->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8375->mclk), "unable to get mclk\n");
+
+ if (!es8375->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(dev, "unable to enable mclk\n");
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8375->mclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8375_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8375_priv *es8375;
+ struct device *dev = &i2c_client->dev;
+ int ret;
+ unsigned int val;
+
+ es8375 = devm_kzalloc(&i2c_client->dev, sizeof(*es8375), GFP_KERNEL);
+ if (!es8375)
+ return -ENOMEM;
+
+ es8375->regmap = devm_regmap_init_i2c(i2c_client,
+ &es8375_regmap_config);
+ if (IS_ERR(es8375->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8375->regmap),
+ "regmap_init() failed\n");
+
+ i2c_set_clientdata(i2c_client, es8375);
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID1, &val);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+
+ if (val != 0x83) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID0, &val);
+ if (val != 0x75) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = es8375_read_device_properities(dev, es8375);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "get an error from dts info %X\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev, &es8375_codec_driver,
+ &es8375_dai, 1);
+}
+
+static void es8375_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8375_priv *es8375;
+
+ es8375 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3C);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x80);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3E);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x15);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x0C);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ clk_disable_unprepare(es8375->mclk);
+}
+
+static const struct i2c_device_id es8375_id[] = {
+ {"es8375"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8375_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8375_acpi_match[] = {
+ {"ESSX8375", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, es8375_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8375_of_match[] = {
+ {.compatible = "everest,es8375",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, es8375_of_match);
+#endif
+
+static struct i2c_driver es8375_i2c_driver = {
+ .driver = {
+ .name = "es8375",
+ .of_match_table = of_match_ptr(es8375_of_match),
+ .acpi_match_table = ACPI_PTR(es8375_acpi_match),
+ },
+ .shutdown = es8375_i2c_shutdown,
+ .probe = es8375_i2c_probe,
+ .id_table = es8375_id,
+};
+module_i2c_driver(es8375_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8375 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h
new file mode 100644
index 000000000000..11e3ceec9b68
--- /dev/null
+++ b/sound/soc/codecs/es8375.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8375.h -- ES8375 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8375.h by Michael Zhang
+*/
+#ifndef _ES8375_H
+#define _ES8375_H
+
+// Registors
+#define ES8375_RESET1 0x00
+#define ES8375_MCLK_SEL 0x01
+#define ES8375_CLK_MGR2 0x02
+#define ES8375_CLK_MGR3 0x03
+#define ES8375_CLK_MGR4 0x04
+#define ES8375_CLK_MGR5 0x05
+#define ES8375_CLK_MGR6 0x06
+#define ES8375_CLK_MGR7 0x07
+#define ES8375_CLK_MGR8 0x08
+#define ES8375_CLK_MGR9 0x09
+#define ES8375_CLK_MGR10 0x0A
+#define ES8375_CLK_MGR11 0x0B
+#define ES8375_CLK_MGR12 0x0C
+#define ES8375_DIV_SPKCLK 0x0E
+#define ES8375_CSM1 0x0F
+#define ES8375_CSM2 0x10
+#define ES8375_VMID_CHARGE2 0x11
+#define ES8375_VMID_CHARGE3 0x12
+#define ES8375_SDP 0x15
+#define ES8375_SDP2 0x16
+#define ES8375_ADC1 0x17
+#define ES8375_ADC2 0x18
+#define ES8375_ADC_OSR_GAIN 0x19
+#define ES8375_ADC_VOLUME 0x1A
+#define ES8375_ADC_AUTOMUTE 0x1B
+#define ES8375_ADC_AUTOMUTE_ATTN 0x1C
+#define ES8375_HPF1 0x1D
+#define ES8375_DAC1 0x1F
+#define ES8375_DAC2 0x20
+#define ES8375_DAC_VOLUME 0x21
+#define ES8375_DAC_VPPSCALE 0x22
+#define ES8375_DAC_AUTOMUTE1 0x23
+#define ES8375_DAC_AUTOMUTE 0x24
+#define ES8375_DAC_CAL 0x25
+#define ES8375_DAC_OTP 0x27
+#define ES8375_ANALOG_SPK1 0x28
+#define ES8375_ANALOG_SPK2 0x29
+#define ES8375_VMID_SEL 0x2D
+#define ES8375_ANALOG1 0x2E
+#define ES8375_ANALOG2 0x32
+#define ES8375_ANALOG3 0x37
+#define ES8375_ADC2DAC_CLKTRI 0xF8
+#define ES8375_SYS_CTRL2 0xF9
+#define ES8375_FLAGS2 0xFB
+#define ES8375_SPK_OFFSET 0xFC
+#define ES8375_CHIP_ID1 0xFD
+#define ES8375_CHIP_ID0 0xFE
+#define ES8375_CHIP_VERSION 0xFF
+
+// Bit Shifts
+#define ADC_OSR_GAIN_SHIFT_0 0
+#define ADC_RAMPRATE_SHIFT_0 0
+#define ADC_VOLUME_SHIFT_0 0
+#define ADC_AUTOMUTE_NG_SHIFT_0 0
+#define ADC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DAC_RAMPRATE_SHIFT_0 0
+#define DAC_VOLUME_SHIFT_0 0
+#define DAC_VPPSCALE_SHIFT_0 0
+#define DAC_AUTOMUTE_NG_SHIFT_0 0
+#define DAC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DMIC_GAIN_SHIFT_2 2
+#define ADC_AUTOMUTE_WS_SHIFT_3 3
+#define DMIC_POL_SHIFT_4 4
+#define DAC_RAMCLR_SHIFT_4 4
+#define ES8375_EN_MODL_SHIFT_4 4
+#define ADC_RAMCLR_SHIFT_5 5
+#define ADC_HPF_SHIFT_5 5
+#define DAC_INV_SHIFT_5 5
+#define DAC_AUTOMUTE_WS_SHIFT_5 5
+#define ES8375_EN_PGAL_SHIFT_5 5
+#define ES8375_ADC_P2S_MUTE_SHIFT_5 5
+#define ADC_INV_SHIFT_6 6
+#define DAC_DEMMUTE_SHIFT_6 6
+#define ES8375_DAC_S2P_MUTE_SHIFT_6 6
+#define ADC_SRC_SHIFT_7 7
+#define ADC_AUTOMUTE_SHIFT_7 7
+#define DAC_DSMMUTE_SHIFT_7 7
+#define DAC_AUTOMUTE_EN_SHIFT_7 7
+
+// Function values
+#define ES8375_ADC_OSR_GAIN_MAX 0x3F
+#define ES8375_DMIC_GAIN_MAX 0x04
+#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F
+#define ES8375_AUTOMUTE_NG_MAX 0x07
+#define ES8375_ADC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VPPSCALE_MAX 0x3F
+#define ES8375_DAC_AUTOMUTE_ATTN_MAX 0x17
+#define ES8375_REG_MAX 0xFF
+
+enum ES8375_supplies {
+ ES8375_SUPPLY_VD = 0,
+ ES8375_SUPPLY_VA,
+};
+
+// Properties
+#define ES8375_3V3 1
+#define ES8375_1V8 0
+
+#define ES8375_MCLK_PIN 0
+#define ES8375_BCLK_PIN 1
+#define ES8375_MCLK_SOURCE ES8375_MCLK_PIN
+
+#define DMIC_POSITIVE_EDGE 0
+#define DMIC_NEGATIVE_EDGE 1
+#define DMIC_POL DMIC_POSITIVE_EDGE
+
+#define PA_SHUTDOWN 0
+#define PA_ENABLE 1
+
+#endif
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
new file mode 100644
index 000000000000..6e4c75d288ef
--- /dev/null
+++ b/sound/soc/codecs/es8389.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8389.c -- ES8389 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "es8389.h"
+
+
+/* codec private data */
+
+struct es8389_private {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int sysclk;
+ int mastermode;
+
+ u8 mclk_src;
+ enum snd_soc_bias_level bias_level;
+};
+
+static bool es8389_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ if ((reg <= 0xff))
+ return true;
+ else
+ return false;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0);
+
+static int es8389_dmic_set(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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ bool changed1, changed2;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > 1)
+ return -EINVAL;
+
+ if (val) {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0xC0, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x03, &changed2);
+ } else {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00, &changed2);
+ }
+
+ if (changed1 & changed2)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL);
+ else
+ return 0;
+}
+
+static const char *const alc[] = {
+ "ALC OFF",
+ "ADCR ALC ON",
+ "ADCL ALC ON",
+ "ADCL & ADCL ALC ON",
+};
+
+static const char *const ramprate[] = {
+ "0.125db/1 LRCK",
+ "0.125db/4 LRCK",
+ "0.125db/8 LRCK",
+ "0.125db/16 LRCK",
+ "0.125db/32 LRCK",
+ "0.125db/64 LRCK",
+ "0.125db/128 LRCK",
+ "0.125db/256 LRCK",
+ "0.125db/512 LRCK",
+ "0.125db/1024 LRCK",
+ "0.125db/2048 LRCK",
+ "0.125db/4096 LRCK",
+ "0.125db/8192 LRCK",
+ "0.125db/16384 LRCK",
+ "0.125db/32768 LRCK",
+ "0.125db/65536 LRCK",
+};
+
+static const char *const winsize[] = {
+ "2 LRCK",
+ "4 LRCK",
+ "8 LRCK",
+ "16 LRCK",
+ "32 LRCK",
+ "64 LRCK",
+ "128 LRCK",
+ "256 LRCK",
+ "512 LRCK",
+ "1024 LRCK",
+ "2048 LRCK",
+ "4096 LRCK",
+ "8192 LRCK",
+ "16384 LRCK",
+ "32768 LRCK",
+ "65536 LRCK",
+};
+
+static const struct soc_enum alc_enable =
+ SOC_ENUM_SINGLE(ES8389_ALC_ON, 5, 4, alc);
+static const struct soc_enum alc_ramprate =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 4, 16, ramprate);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize);
+
+static const char *const es8389_outl_mux_txt[] = {
+ "Normal",
+ "DAC2 channel to DAC1 channel",
+};
+
+static const char *const es8389_outr_mux_txt[] = {
+ "Normal",
+ "DAC1 channel to DAC2 channel",
+};
+
+static const char *const es8389_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static const char *const es8389_pga1_texts[] = {
+ "DifferentialL", "Line 1P", "Line 2P"
+};
+
+static const char *const es8389_pga2_texts[] = {
+ "DifferentialR", "Line 2N", "Line 1N"
+};
+
+static const unsigned int es8389_pga_values[] = {
+ 1, 5, 6
+};
+
+static const struct soc_enum es8389_outl_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5,
+ ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outl_mux_controls =
+ SOC_DAPM_ENUM("OUTL MUX", es8389_outl_mux_enum);
+
+static const struct soc_enum es8389_outr_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 4,
+ ARRAY_SIZE(es8389_outr_mux_txt), es8389_outr_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outr_mux_controls =
+ SOC_DAPM_ENUM("OUTR MUX", es8389_outr_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ es8389_dmic_mux_enum, ES8389_DMIC_EN, 6, es8389_dmic_mux_txt);
+
+static const struct soc_enum es8389_pgal_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC1_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga1_texts), es8389_pga1_texts,
+ es8389_pga_values);
+
+static const struct soc_enum es8389_pgar_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC2_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga2_texts), es8389_pga2_texts,
+ es8389_pga_values);
+
+static const struct snd_kcontrol_new es8389_dmic_mux_controls =
+ SOC_DAPM_ENUM_EXT("ADC MUX", es8389_dmic_mux_enum,
+ snd_soc_dapm_get_enum_double, es8389_dmic_set);
+
+static const struct snd_kcontrol_new es8389_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR DACL Mixer", ES8389_DAC_MIX, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL DACR Mixer", ES8389_DAC_MIX, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_leftadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCL DACL Mixer", ES8389_DAC_MIX, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_rightadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCR DACR Mixer", ES8389_DAC_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL ADCL Mixer", ES8389_ADC_RESET, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR ADCR Mixer", ES8389_ADC_RESET, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_snd_controls[] = {
+ SOC_SINGLE_TLV("ADCL Capture Volume", ES8389_ADCL_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCR Capture Volume", ES8389_ADCR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCL PGA Volume", ES8389_MIC1_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+ SOC_SINGLE_TLV("ADCR PGA Volume", ES8389_MIC2_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+
+ SOC_ENUM("PGAL Select", es8389_pgal_enum),
+ SOC_ENUM("PGAR Select", es8389_pgar_enum),
+ SOC_ENUM("ALC Capture Switch", alc_enable),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8389_ALC_TARGET,
+ 0, 0x0f, 0, alc_target_tlv),
+ SOC_SINGLE_TLV("ALC Capture Max Gain", ES8389_ALC_GAIN,
+ 0, 0x0f, 0, alc_max_level),
+ SOC_ENUM("ADC Ramp Rate", alc_ramprate),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0),
+
+ SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC OUTPUT Invert Switch", ES8389_DAC_INV, 5, 6, 1, 0),
+ SOC_SINGLE_TLV("ADC2DAC Mixer Volume", ES8389_MIX_VOL, 0, 0x7F, 0, mix_vol_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = {
+ /*Input Side*/
+ SND_SOC_DAPM_INPUT("INPUT1"),
+ SND_SOC_DAPM_INPUT("INPUT2"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGAL", SND_SOC_NOPM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAR", SND_SOC_NOPM, 4, 0, NULL, 0),
+
+ /*ADCs*/
+ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /*DACs*/
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /*Output Side*/
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MIXER("IF DACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_left_mixer_controls[0],
+ ARRAY_SIZE(es8389_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF DACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_right_mixer_controls[0],
+ ARRAY_SIZE(es8389_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_leftadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_leftadc_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_rightadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_rightadc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_adc_mixer_controls[0],
+ ARRAY_SIZE(es8389_adc_mixer_controls)),
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls),
+
+ SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls),
+ SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls),
+};
+
+
+static const struct snd_soc_dapm_route es8389_dapm_routes[] = {
+ {"PGAL", NULL, "INPUT1"},
+ {"PGAR", NULL, "INPUT2"},
+
+ {"ADCL", NULL, "PGAL"},
+ {"ADCR", NULL, "PGAR"},
+
+ {"ADC Mixer", "DACL ADCL Mixer", "DACL"},
+ {"ADC Mixer", "DACR ADCR Mixer", "DACR"},
+ {"ADC Mixer", NULL, "ADCL"},
+ {"ADC Mixer", NULL, "ADCR"},
+
+ {"ADC MUX", "AMIC", "ADC Mixer"},
+ {"ADC MUX", "DMIC", "DMIC"},
+
+ {"I2S OUT", NULL, "ADC MUX"},
+
+ {"DACL", NULL, "I2S IN"},
+ {"DACR", NULL, "I2S IN"},
+
+ {"IF DACL1", NULL, "DACL"},
+ {"IF DACR1", NULL, "DACR"},
+ {"IF DACL2", NULL, "DACL"},
+ {"IF DACR2", NULL, "DACR"},
+ {"IF DACL3", NULL, "DACL"},
+ {"IF DACR3", NULL, "DACR"},
+
+ {"IF DACL Mixer", NULL, "IF DACL2"},
+ {"IF DACL Mixer", "DACR DACL Mixer", "IF DACR1"},
+ {"IF DACR Mixer", NULL, "IF DACR2"},
+ {"IF DACR Mixer", "DACL DACR Mixer", "IF DACL1"},
+
+ {"IF ADCDACL Mixer", NULL, "IF DACL Mixer"},
+ {"IF ADCDACL Mixer", "ADCL DACL Mixer", "IF DACL3"},
+ {"IF ADCDACR Mixer", NULL, "IF DACR Mixer"},
+ {"IF ADCDACR Mixer", "ADCR DACR Mixer", "IF DACR3"},
+
+ {"OUTL MUX", "Normal", "IF ADCDACL Mixer"},
+ {"OUTL MUX", "DAC2 channel to DAC1 channel", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "Normal", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "DAC1 channel to DAC2 channel", "IF ADCDACL Mixer"},
+
+ {"HPOL", NULL, "OUTL MUX"},
+ {"HPOR", NULL, "OUTR MUX"},
+
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0F;
+ u8 Reg0x11;
+ u8 Reg0x21;
+ u8 Reg0x22;
+ u8 Reg0x26;
+ u8 Reg0x30;
+ u8 Reg0x41;
+ u8 Reg0x42;
+ u8 Reg0x43;
+ u8 Reg0xF0;
+ u8 Reg0xF1;
+ u8 Reg0x16;
+ u8 Reg0x18;
+ u8 Reg0x19;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
+
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+};
+
+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;
+}
+
+/*
+ * if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source.
+ */
+static int es8389_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389->sysclk = freq;
+
+ return 0;
+}
+
+static int es8389_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(es8389->regmap, ES8389_PTDM_SLOT,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+ regmap_update_bits(es8389->regmap, ES8389_DAC_RAMP,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+
+ return 0;
+}
+
+static int es8389_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ u8 state = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_MODE,
+ ES8389_MASTER_MODE_EN, ES8389_MASTER_MODE_EN);
+ es8389->mastermode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8389->mastermode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ state |= ES8389_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "component driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ state |= ES8389_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ state |= ES8389_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ state |= ES8389_DAIFMT_DSP_B;
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+
+ return 0;
+}
+
+static int es8389_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ int coeff;
+ u8 state = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ state |= ES8389_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ state |= ES8389_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ state |= ES8389_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ state |= ES8389_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ state |= ES8389_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+
+ if (es8389->mclk_src == ES8389_SCLK_PIN) {
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK,
+ ES8389_MCLK_SOURCE, es8389->mclk_src);
+ es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
+ }
+
+ coeff = get_coeff(es8389->sysclk, params_rate(params));
+ if (coeff >= 0) {
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, coeff_div[coeff].Reg0x06);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, coeff_div[coeff].Reg0x07);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, coeff_div[coeff].Reg0x08);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, coeff_div[coeff].Reg0x09);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, coeff_div[coeff].Reg0x0A);
+ regmap_update_bits(es8389->regmap, ES8389_OSC_CLK,
+ 0xC0, coeff_div[coeff].Reg0x0F);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV2, coeff_div[coeff].Reg0x11);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, coeff_div[coeff].Reg0x21);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, coeff_div[coeff].Reg0x22);
+ regmap_write(es8389->regmap, ES8389_OSR_VOL, coeff_div[coeff].Reg0x26);
+ regmap_update_bits(es8389->regmap, ES8389_SYSTEM30,
+ 0xC0, coeff_div[coeff].Reg0x30);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, coeff_div[coeff].Reg0x41);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, coeff_div[coeff].Reg0x42);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_MISC,
+ 0x81, coeff_div[coeff].Reg0x43);
+ regmap_update_bits(es8389->regmap, ES8389_CHIP_MISC,
+ 0x72, coeff_div[coeff].Reg0xF0);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, coeff_div[coeff].Reg0xF1);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, coeff_div[coeff].Reg0x16);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, coeff_div[coeff].Reg0x18);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, coeff_div[coeff].Reg0x19);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8389_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x3E);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
+ usleep_range(8000, 8500);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
+
+ clk_disable_unprepare(es8389->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+
+
+static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x03);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x03);
+ }
+ } else {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x00);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+#define es8389_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8389_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 es8389_ops = {
+ .hw_params = es8389_pcm_hw_params,
+ .set_fmt = es8389_set_dai_fmt,
+ .set_sysclk = es8389_set_dai_sysclk,
+ .set_tdm_slot = es8389_set_tdm_slot,
+ .mute_stream = es8389_mute,
+};
+
+static struct snd_soc_dai_driver es8389_dai = {
+ .name = "ES8389 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .ops = &es8389_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8389_init(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF2, 0x04);
+ regmap_write(es8389->regmap, ES8389_DAC_INV, 0x03);
+
+ regmap_write(es8389->regmap, ES8389_VMID, 0x2A);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xC9);
+ regmap_write(es8389->regmap, ES8389_ANA_VSEL, 0x4F);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL2, 0x06);
+ regmap_write(es8389->regmap, ES8389_LOW_POWER1, 0x00);
+ regmap_write(es8389->regmap, ES8389_DMIC_EN, 0x16);
+
+ regmap_write(es8389->regmap, ES8389_PGA_SW, 0xAA);
+ regmap_write(es8389->regmap, ES8389_MOD_SW1, 0x66);
+ regmap_write(es8389->regmap, ES8389_MOD_SW2, 0x99);
+ regmap_write(es8389->regmap, ES8389_ADC_MODE, (0x00 | ES8389_TDM_MODE));
+ regmap_update_bits(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00);
+
+ regmap_update_bits(es8389->regmap, ES8389_MIC1_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_update_bits(es8389->regmap, ES8389_MIC2_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xC4);
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x08);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, 0x00);
+ regmap_write(es8389->regmap, ES8389_SYSTEM12, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM13, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM14, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM15, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, 0x35);
+ regmap_write(es8389->regmap, ES8389_SYSTEM17, 0x09);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, 0x91);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, 0x28);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1A, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1B, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1C, 0x11);
+
+ regmap_write(es8389->regmap, ES8389_CHIP_MISC, 0x13);
+ regmap_write(es8389->regmap, ES8389_MASTER_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, 0x10);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, 0x80);
+ regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00);
+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F);
+ regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0);
+ regmap_write(es8389->regmap, ES8389_SYSTEM30, 0xF4);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_MISC, 0x10);
+ regmap_write(es8389->regmap, ES8389_DAC_RAMP, 0x0F);
+ regmap_write(es8389->regmap, ES8389_SYSTEM4C, 0xC0);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC1);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0x02);
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x03);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x03);
+}
+
+static int es8389_suspend(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+ regcache_cache_only(es8389->regmap, true);
+ regcache_mark_dirty(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_resume(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
+
+ regcache_cache_only(es8389->regmap, false);
+ regcache_cache_bypass(es8389->regmap, true);
+ regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ regcache_cache_bypass(es8389->regmap, false);
+
+ if (regv == 0xff)
+ es8389_init(component);
+ else
+ es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mclk-src return %d", ret);
+ es8389->mclk_src = ES8389_MCLK_SOURCE;
+ }
+
+ es8389->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(es8389->mclk))
+ return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
+ "ES8389 is unable to get mclk\n");
+
+ if (!es8389->mclk)
+ dev_err(component->dev, "%s, assuming static mclk\n", __func__);
+
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret) {
+ dev_err(component->dev, "%s, unable to enable mclk\n", __func__);
+ return ret;
+ }
+
+ es8389_init(component);
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static void es8389_remove(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_es8389 = {
+ .probe = es8389_probe,
+ .remove = es8389_remove,
+ .suspend = es8389_suspend,
+ .resume = es8389_resume,
+ .set_bias_level = es8389_set_bias_level,
+
+ .controls = es8389_snd_controls,
+ .num_controls = ARRAY_SIZE(es8389_snd_controls),
+ .dapm_widgets = es8389_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8389_dapm_widgets),
+ .dapm_routes = es8389_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8389_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+};
+
+static const struct regmap_config es8389_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ES8389_MAX_REGISTER,
+
+ .volatile_reg = es8389_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static void es8389_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8389_private *es8389;
+
+ es8389 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+}
+
+static int es8389_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8389_private *es8389;
+ int ret;
+
+ es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL);
+ if (es8389 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8389);
+ es8389->regmap = devm_regmap_init_i2c(i2c_client, &es8389_regmap);
+ if (IS_ERR(es8389->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap),
+ "regmap_init() failed\n");
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_codec_dev_es8389,
+ &es8389_dai,
+ 1);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8389_if_dt_ids[] = {
+ { .compatible = "everest,es8389", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8389_if_dt_ids);
+#endif
+
+static const struct i2c_device_id es8389_i2c_id[] = {
+ {"es8389"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8389_i2c_id);
+
+static struct i2c_driver es8389_i2c_driver = {
+ .driver = {
+ .name = "es8389",
+ .of_match_table = of_match_ptr(es8389_if_dt_ids),
+ },
+ .shutdown = es8389_i2c_shutdown,
+ .probe = es8389_i2c_probe,
+ .id_table = es8389_i2c_id,
+};
+module_i2c_driver(es8389_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8389 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
new file mode 100644
index 000000000000..123d1e4b2d53
--- /dev/null
+++ b/sound/soc/codecs/es8389.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8389.h -- ES8389 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8374.h by Michael Zhang
+*/
+
+#ifndef _ES8389_H
+#define _ES8389_H
+
+/*
+* ES8389_REGISTER NAME_REG_REGISTER ADDRESS
+*/
+#define ES8389_RESET 0x00 /*reset digital,csm,clock manager etc.*/
+
+/*
+* Clock Scheme Register definition
+*/
+#define ES8389_MASTER_MODE 0x01
+#define ES8389_MASTER_CLK 0x02
+#define ES8389_CLK_OFF1 0x03
+#define ES8389_CLK_DIV1 0x04
+#define ES8389_CLK_MUL 0x05
+#define ES8389_CLK_MUX1 0x06
+#define ES8389_CLK_MUX2 0x07
+#define ES8389_CLK_CTL1 0x08
+#define ES8389_CLK_CTL2 0x09
+#define ES8389_CLK_CTL3 0x0A
+#define ES8389_SCLK_DIV 0x0B
+#define ES8389_LRCK_DIV1 0x0C
+#define ES8389_LRCK_DIV2 0x0D
+#define ES8389_CLK_OFF2 0x0E
+#define ES8389_OSC_CLK 0x0F
+#define ES8389_CSM_JUMP 0x10
+#define ES8389_CLK_DIV2 0x11
+#define ES8389_SYSTEM12 0x12
+#define ES8389_SYSTEM13 0x13
+#define ES8389_SYSTEM14 0x14
+#define ES8389_SYSTEM15 0x15
+#define ES8389_SYSTEM16 0x16
+#define ES8389_SYSTEM17 0x17
+#define ES8389_SYSTEM18 0x18
+#define ES8389_SYSTEM19 0x19
+#define ES8389_SYSTEM1A 0x1A
+#define ES8389_SYSTEM1B 0x1B
+#define ES8389_SYSTEM1C 0x1C
+#define ES8389_ADC_FORMAT_MUTE 0x20
+#define ES8389_ADC_OSR 0x21
+#define ES8389_ADC_DSP 0x22
+#define ES8389_ADC_MODE 0x23
+#define ES8389_ADC_HPF1 0x24
+#define ES8389_ADC_HPF2 0x25
+#define ES8389_OSR_VOL 0x26
+#define ES8389_ADCL_VOL 0x27
+#define ES8389_ADCR_VOL 0x28
+#define ES8389_ALC_CTL 0x29
+#define ES8389_PTDM_SLOT 0x2A
+#define ES8389_ALC_ON 0x2B
+#define ES8389_ALC_TARGET 0x2C
+#define ES8389_ALC_GAIN 0x2D
+#define ES8389_SYSTEM2E 0x2E
+#define ES8389_ADC_MUTE 0x2F
+#define ES8389_SYSTEM30 0x30
+#define ES8389_ADC_RESET 0x31
+#define ES8389_DAC_FORMAT_MUTE 0x40
+#define ES8389_DAC_DSM_OSR 0x41
+#define ES8389_DAC_DSP_OSR 0x42
+#define ES8389_DAC_MISC 0x43
+#define ES8389_DAC_MIX 0x44
+#define ES8389_DAC_INV 0x45
+#define ES8389_DACL_VOL 0x46
+#define ES8389_DACR_VOL 0x47
+#define ES8389_MIX_VOL 0x48
+#define ES8389_DAC_RAMP 0x49
+#define ES8389_SYSTEM4C 0x4C
+#define ES8389_DAC_RESET 0x4D
+#define ES8389_VMID 0x60
+#define ES8389_ANA_CTL1 0x61
+#define ES8389_ANA_VSEL 0x62
+#define ES8389_ANA_CTL2 0x63
+#define ES8389_ADC_EN 0x64
+#define ES8389_HPSW 0x69
+#define ES8389_LOW_POWER1 0x6B
+#define ES8389_LOW_POWER2 0x6C
+#define ES8389_DMIC_EN 0x6D
+#define ES8389_PGA_SW 0x6E
+#define ES8389_MOD_SW1 0x6F
+#define ES8389_MOD_SW2 0x70
+#define ES8389_MOD_SW3 0x71
+#define ES8389_MIC1_GAIN 0x72
+#define ES8389_MIC2_GAIN 0x73
+
+#define ES8389_CHIP_MISC 0xF0
+#define ES8389_CSM_STATE1 0xF1
+#define ES8389_PULL_DOWN 0xF2
+#define ES8389_ISO_CTL 0xF3
+#define ES8389_CSM_STATE2 0xF4
+
+#define ES8389_CHIP_ID0 0xFD
+#define ES8389_CHIP_ID1 0xFE
+
+#define ES8389_MAX_REGISTER 0xFF
+
+#define ES8389_MIC_SEL_MASK (7 << 4)
+#define ES8389_MIC_DEFAULT (1 << 4)
+
+#define ES8389_MASTER_MODE_EN (1 << 0)
+
+#define ES8389_TDM_OFF (0 << 0)
+#define ES8389_STDM_ON (1 << 7)
+#define ES8389_PTDM_ON (1 << 6)
+
+#define ES8389_TDM_MODE ES8389_TDM_OFF
+#define ES8389_TDM_SLOT (0x70 << 0)
+#define ES8389_TDM_SHIFT 4
+
+#define ES8389_MCLK_SOURCE (1 << 6)
+#define ES8389_MCLK_PIN (1 << 6)
+#define ES8389_SCLK_PIN (0 << 6)
+
+/* ES8389_FMT */
+#define ES8389_S24_LE (0 << 5)
+#define ES8389_S20_3_LE (1 << 5)
+#define ES8389_S18_LE (2 << 5)
+#define ES8389_S16_LE (3 << 5)
+#define ES8389_S32_LE (4 << 5)
+#define ES8389_DATA_LEN_MASK (7 << 5)
+
+#define ES8389_DAIFMT_MASK (7 << 2)
+#define ES8389_DAIFMT_I2S 0
+#define ES8389_DAIFMT_LEFT_J (1 << 2)
+#define ES8389_DAIFMT_DSP_A (1 << 3)
+#define ES8389_DAIFMT_DSP_B (3 << 3)
+
+#define ES8389_STATE_ON (13 << 0)
+#define ES8389_STATE_STANDBY (7 << 0)
+
+#endif
diff --git a/sound/soc/codecs/es83xx-dsm-common.c b/sound/soc/codecs/es83xx-dsm-common.c
new file mode 100644
index 000000000000..94fd7d54c53b
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) Intel Corporation, 2022
+// Copyright Everest Semiconductor Co.,Ltd
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "es83xx-dsm-common.h"
+
+/* UUID ("a9800c04-e016-343e-41f4-6bcce70f4332") */
+static const guid_t es83xx_dsm_guid =
+ GUID_INIT(0xa9800c04, 0xe016, 0x343e,
+ 0x41, 0xf4, 0x6b, 0xcc, 0xe7, 0x0f, 0x43, 0x32);
+
+#define ES83xx_DSM_REVID 1
+
+int es83xx_dsm(struct device *dev, int arg, int *value)
+{
+ acpi_handle dhandle;
+ union acpi_object *obj;
+ int ret = 0;
+
+ dhandle = ACPI_HANDLE(dev);
+ if (!dhandle)
+ return -ENOENT;
+
+ obj = acpi_evaluate_dsm(dhandle, &es83xx_dsm_guid, ES83xx_DSM_REVID,
+ arg, NULL);
+ if (!obj) {
+ dev_err(dev, "%s: acpi_evaluate_dsm() failed\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ dev_err(dev, "%s: object is not ACPI_TYPE_INTEGER\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ *value = obj->integer.value;
+err:
+ ACPI_FREE(obj);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm);
+
+int es83xx_dsm_dump(struct device *dev)
+{
+ int value;
+ int ret;
+
+ ret = es83xx_dsm(dev, PLATFORM_MAINMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MAINMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPMIC_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPMIC_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_SPK_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_SPK_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_HPDET_INV_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_HPDET_INV %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_PCM_TYPE_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_PCM_TYPE %#x\n", value);
+
+ ret = es83xx_dsm(dev, PLATFORM_MIC_DE_POP_ARG, &value);
+ if (ret < 0)
+ return ret;
+ dev_info(dev, "PLATFORM_MIC_DE_POP %#x\n", value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(es83xx_dsm_dump);
+
+MODULE_DESCRIPTION("Everest Semi ES83xx DSM helpers");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es83xx-dsm-common.h b/sound/soc/codecs/es83xx-dsm-common.h
new file mode 100644
index 000000000000..91c9a89e75e9
--- /dev/null
+++ b/sound/soc/codecs/es83xx-dsm-common.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) Intel Corporation, 2022
+ * Copyright Everest Semiconductor Co.,Ltd
+ */
+
+/* Definitions extracted from ASL file provided at
+ * https://github.com/thesofproject/linux/files/9398723/ESSX8326.zip
+ */
+
+#ifndef _ES83XX_DSM_COMMON_H
+#define _ES83XX_DSM_COMMON_H
+
+/***************************************************
+ * DSM arguments *
+ ***************************************************/
+
+#define PLATFORM_MAINMIC_TYPE_ARG 0x00
+#define PLATFORM_HPMIC_TYPE_ARG 0x01
+#define PLATFORM_SPK_TYPE_ARG 0x02
+#define PLATFORM_HPDET_INV_ARG 0x03
+#define PLATFORM_PCM_TYPE_ARG 0x04
+
+#define PLATFORM_MIC_DE_POP_ARG 0x06
+#define PLATFORM_CODEC_TYPE_ARG 0x0E
+#define PLATFORM_BUS_SLOT_ARG 0x0F
+
+#define HP_CODEC_LINEIN_PGA_GAIN_ARG 0x10
+#define MAIN_CODEC_LINEIN_PGA_GAIN_ARG 0x20
+
+#define HP_CODEC_D2SEPGA_GAIN_ARG 0x11
+#define MAIN_CODEC_D2SEPGA_GAIN_ARG 0x21
+
+#define HP_CODEC_ADC_VOLUME_ARG 0x12
+#define MAIN_CODEC_ADC_VOLUME_ARG 0x22
+
+#define HP_CODEC_ADC_ALC_ENABLE_ARG 0x13
+#define MAIN_CODEC_ADC_ALC_ENABLE_ARG 0x23
+
+#define HP_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x14
+#define MAIN_CODEC_ADC_ALC_TARGET_LEVEL_ARG 0x24
+
+#define HP_CODEC_ADC_ALC_MAXGAIN_ARG 0x15
+#define MAIN_CODEC_ADC_ALC_MAXGAIN_ARG 0x25
+
+#define HP_CODEC_ADC_ALC_MINGAIN_ARG 0x16
+#define MAIN_CODEC_ADC_ALC_MINGAIN_ARG 0x26
+
+#define HP_CODEC_ADC_ALC_HLDTIME_ARG 0x17
+#define MAIN_CODEC_ADC_ALC_HLDTIME_ARG 0x27
+
+#define HP_CODEC_ADC_ALC_DCYTIME_ARG 0x18
+#define MAIN_CODEC_ADC_ALC_DCYTIME_ARG 0x28
+
+#define HP_CODEC_ADC_ALC_ATKTIME_ARG 0x19
+#define MAIN_CODEC_ADC_ALC_ATKTIME_ARG 0x29
+
+#define HP_CODEC_ADC_ALC_NGTYPE_ARG 0x1a
+#define MAIN_CODEC_ADC_ALC_NGTYPE_ARG 0x2a
+
+#define HP_CODEC_ADC_ALC_NGTHLD_ARG 0x1b
+#define MAIN_CODEC_ADC_ALC_NGTHLD_ARG 0x2b
+
+#define MAIN_CODEC_ADC_GUI_STEP_ARG 0x2c
+#define MAIN_CODEC_ADC_GUI_GAIN_RANGE_ARG 0x2c
+
+#define HEADPHONE_DUMMY_REMOVE_ENABLE_ARG 0x2e
+
+#define HP_CODEC_DAC_HPMIX_HIGAIN_ARG 0x40
+#define SPK_CODEC_DAC_HPMIX_HIGAIN_ARG 0x50
+
+#define HP_CODEC_DAC_HPMIX_VOLUME_ARG 0x41
+#define SPK_CODEC_DAC_HPMIX_VOLUME_ARG 0x51
+
+#define HP_CODEC_DAC_HPOUT_VOLUME_ARG 0x42
+#define SPK_CODEC_DAC_HPOUT_VOLUME_ARG 0x52
+
+#define HP_CODEC_LDAC_VOLUME_ARG 0x44
+#define HP_CODEC_RDAC_VOLUME_ARG 0x54
+
+#define SPK_CODEC_LDAC_VOLUME_ARG 0x45
+#define SPK_CODEC_RDAC_VOLUME_ARG 0x55
+
+#define HP_CODEC_DAC_AUTOMUTE_ARG 0x46
+#define SPK_CODEC_DAC_AUTOMUTE_ARG 0x56
+
+#define HP_CODEC_DAC_MONO_ARG 0x4A
+#define SPK_CODEC_DAC_MONO_ARG 0x5A
+
+#define HP_CTL_IO_LEVEL_ARG 0x4B
+#define SPK_CTL_IO_LEVEL_ARG 0x5B
+
+#define CODEC_GPIO0_FUNC_ARG 0x80
+#define CODEC_GPIO1_FUNC_ARG 0x81
+#define CODEC_GPIO2_FUNC_ARG 0x82
+#define CODEC_GPIO3_FUNC_ARG 0x83
+#define CODEC_GPIO4_FUNC_ARG 0x84
+
+#define PLATFORM_MCLK_LRCK_FREQ_ARG 0x85
+
+/***************************************************
+ * Values for arguments *
+ ***************************************************/
+
+/* Main and HP Mic */
+#define PLATFORM_MIC_DMIC_HIGH_LEVEL 0xAA
+#define PLATFORM_MIC_DMIC_LOW_LEVEL 0x55
+#define PLATFORM_MIC_AMIC_LIN1RIN1 0xBB
+#define PLATFORM_MIC_AMIC_LIN2RIN2 0xCC
+
+/* Speaker */
+#define PLATFORM_SPK_NONE 0x00
+#define PLATFORM_SPK_MONO 0x01
+#define PLATFORM_SPK_STEREO 0x02
+
+/* Jack Detection */
+#define PLATFORM_HPDET_NORMAL 0x00
+#define PLATFORM_HPDET_INVERTED 0x01
+
+/* PCM type (Port number + protocol) */
+/*
+ * RETURNED VALUE = 0x00, PCM PORT0, I2S
+ * 0x01, PCM PORT0, LJ
+ * 0x02, PCM PORT0, RJ
+ * 0x03, PCM PORT0, DSP-A
+ * 0x04, PCM PORT0, DSP-B
+ * 0x10, PCM PORT1, I2S
+ * 0x11, PCM PORT1, LJ
+ * 0x12, PCM PORT1, RJ
+ * 0x13, PCM PORT1, DSP-A
+ * 0x14, PCM PORT1, DSP-B
+ * 0xFF, Use default
+ *
+ * This is not used in Linux (defined by topology) and in
+ * Windows it's always DSP-A
+ */
+
+/* Depop */
+#define PLATFORM_MIC_DE_POP_OFF 0x00
+#define PLATFORM_MIC_DE_POP_ON 0x01
+
+/* Codec type */
+#define PLATFORM_CODEC_8316 16
+#define PLATFORM_CODEC_8326 26
+#define PLATFORM_CODEC_8336 36
+#define PLATFORM_CODEC_8395 95
+#define PLATFORM_CODEC_8396 96
+
+/* Bus slot (on the host) */
+/* BIT[3:0] FOR BUS NUMBER, BIT[7:4] FOR SLOT NUMBER
+ * BIT[3:0] 0 for I2S0, 1 for IS21, 2 for I2S2.
+ *
+ * On Intel platforms this refers to SSP0..2. This information
+ * is not really useful for Linux, the information is already
+ * inferred from NHLT but can be used to double-check NHLT
+ */
+
+/* Volume - Gain */
+#define LINEIN_GAIN_0db 0x00 /* gain = 0db */
+#define LINEIN_GAIN_3db 0x01 /* gain = +3db */
+#define LINEIN_GAIN_6db 0x02 /* gain = +6db */
+#define LINEIN_GAIN_9db 0x03 /* gain = +9db */
+#define LINEIN_GAIN_12db 0x04 /* gain = +12db */
+#define LINEIN_GAIN_15db 0x05 /* gain = +15db */
+#define LINEIN_GAIN_18db 0x06 /* gain = +18db */
+#define LINEIN_GAIN_21db 0x07 /* gain = +21db */
+#define LINEIN_GAIN_24db 0x08 /* gain = +24db */
+#define LINEIN_GAIN_27db 0x09 /* gain = +27db */
+#define LINEIN_GAIN_30db 0x0a /* gain = +30db */
+
+#define ADC_GUI_STEP_3db 0x03 /* gain = +3db */
+#define ADC_GUI_STEP_6db 0x06 /* gain = +6db */
+#define ADC_GUI_STEP_10db 0x0a /* gain = +10db */
+
+#define D2SEPGA_GAIN_0db 0x00 /* gain = 0db */
+#define D2SEPGA_GAIN_15db 0x01 /* gain = +15db */
+
+/* ADC volume: base = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+#define ADC_ALC_DISABLE 0x00
+#define ADC_ALC_ENABLE 0x01
+
+#define ADC_ALC_TARGET_LEVEL_m16_5db 0x00 /* gain = -16.5db */
+#define ADC_ALC_TARGET_LEVEL_m15db 0x01 /* gain = -15db */
+#define ADC_ALC_TARGET_LEVEL_m13_5db 0x02 /* gain = -13.5db */
+#define ADC_ALC_TARGET_LEVEL_m12db 0x03 /* gain = -12db */
+#define ADC_ALC_TARGET_LEVEL_m10_5db 0x04 /* gain = -10.5db */
+#define ADC_ALC_TARGET_LEVEL_m9db 0x05 /* gain = -9db */
+#define ADC_ALC_TARGET_LEVEL_m7_5db 0x06 /* gain = -7.5db */
+#define ADC_ALC_TARGET_LEVEL_m6db 0x07 /* gain = -6db */
+#define ADC_ALC_TARGET_LEVEL_m4_5db 0x08 /* gain = -4.5db */
+#define ADC_ALC_TARGET_LEVEL_m_3db 0x09 /* gain = -3db */
+#define ADC_ALC_TARGET_LEVEL_m1_5db 0x0a /* gain = -1.5db */
+
+#define ADC_ALC_MAXGAIN_m6_5db 0x00 /* gain = -6.5db */
+#define ADC_ALC_MAXGAIN_m5db 0x01 /* gain = -5db */
+#define ADC_ALC_MAXGAIN_m3_5db 0x02 /* gain = -3.5db */
+#define ADC_ALC_MAXGAIN_m2db 0x03 /* gain = -2db */
+#define ADC_ALC_MAXGAIN_m0_5db 0x04 /* gain = -0.5db */
+#define ADC_ALC_MAXGAIN_1db 0x05 /* gain = +1db */
+#define ADC_ALC_MAXGAIN_2_5db 0x06 /* gain = +2.5db */
+#define ADC_ALC_MAXGAIN_4db 0x07 /* gain = +4db */
+#define ADC_ALC_MAXGAIN_5_5db 0x08 /* gain = +5.5db */
+#define ADC_ALC_MAXGAIN_7db 0x09 /* gain = +7db */
+#define ADC_ALC_MAXGAIN_8_5db 0x0a /* gain = +8.5db */
+#define ADC_ALC_MAXGAIN_10db 0x0b /* gain = +10db */
+#define ADC_ALC_MAXGAIN_11_5db 0x0c /* gain = +11.5db */
+#define ADC_ALC_MAXGAIN_13db 0x0d /* gain = +13db */
+#define ADC_ALC_MAXGAIN_14_5db 0x0e /* gain = +14.5db */
+#define ADC_ALC_MAXGAIN_16db 0x0f /* gain = +16db */
+#define ADC_ALC_MAXGAIN_17_5db 0x10 /* gain = +17.5db */
+#define ADC_ALC_MAXGAIN_19db 0x11 /* gain = +19db */
+#define ADC_ALC_MAXGAIN_20_5db 0x12 /* gain = +20.5db */
+#define ADC_ALC_MAXGAIN_22db 0x13 /* gain = +22db */
+#define ADC_ALC_MAXGAIN_23_5db 0x14 /* gain = +23.5db */
+#define ADC_ALC_MAXGAIN_25db 0x15 /* gain = +25db */
+#define ADC_ALC_MAXGAIN_26_5db 0x16 /* gain = +26.5db */
+#define ADC_ALC_MAXGAIN_28db 0x17 /* gain = +28db */
+#define ADC_ALC_MAXGAIN_29_5db 0x18 /* gain = +29.5db */
+#define ADC_ALC_MAXGAIN_31db 0x19 /* gain = +31db */
+#define ADC_ALC_MAXGAIN_32_5db 0x1a /* gain = +32.5db */
+#define ADC_ALC_MAXGAIN_34db 0x1b /* gain = +34db */
+#define ADC_ALC_MAXGAIN_35_5db 0x1c /* gain = +35.5db */
+
+#define ADC_ALC_MINGAIN_m12db 0x00 /* gain = -12db */
+#define ADC_ALC_MINGAIN_m10_5db 0x01 /* gain = -10.5db */
+#define ADC_ALC_MINGAIN_m9db 0x02 /* gain = -9db */
+#define ADC_ALC_MINGAIN_m7_5db 0x03 /* gain = -7.5db */
+#define ADC_ALC_MINGAIN_m6db 0x04 /* gain = -6db */
+#define ADC_ALC_MINGAIN_m4_51db 0x05 /* gain = -4.51db */
+#define ADC_ALC_MINGAIN_m3db 0x06 /* gain = -3db */
+#define ADC_ALC_MINGAIN_m1_5db 0x07 /* gain = -1.5db */
+#define ADC_ALC_MINGAIN_0db 0x08 /* gain = 0db */
+#define ADC_ALC_MINGAIN_1_5db 0x09 /* gain = +1.5db */
+#define ADC_ALC_MINGAIN_3db 0x0a /* gain = +3db */
+#define ADC_ALC_MINGAIN_4_5db 0x0b /* gain = +4.5db */
+#define ADC_ALC_MINGAIN_6db 0x0c /* gain = +6db */
+#define ADC_ALC_MINGAIN_7_5db 0x0d /* gain = +7.5db */
+#define ADC_ALC_MINGAIN_9db 0x0e /* gain = +9db */
+#define ADC_ALC_MINGAIN_10_5db 0x0f /* gain = +10.5db */
+#define ADC_ALC_MINGAIN_12db 0x10 /* gain = +12db */
+#define ADC_ALC_MINGAIN_13_5db 0x11 /* gain = +13.5db */
+#define ADC_ALC_MINGAIN_15db 0x12 /* gain = +15db */
+#define ADC_ALC_MINGAIN_16_5db 0x13 /* gain = +16.5db */
+#define ADC_ALC_MINGAIN_18db 0x14 /* gain = +18db */
+#define ADC_ALC_MINGAIN_19_5db 0x15 /* gain = +19.5db */
+#define ADC_ALC_MINGAIN_21db 0x16 /* gain = +21db */
+#define ADC_ALC_MINGAIN_22_5db 0x17 /* gain = +22.5db */
+#define ADC_ALC_MINGAIN_24db 0x18 /* gain = +24db */
+#define ADC_ALC_MINGAIN_25_5db 0x19 /* gain = +25.5db */
+#define ADC_ALC_MINGAIN_27db 0x1a /* gain = +27db */
+#define ADC_ALC_MINGAIN_28_5db 0x1b /* gain = +28.5db */
+#define ADC_ALC_MINGAIN_30db 0x1c /* gain = +30db */
+
+/* ADC volume: step 1dB */
+
+/* ALC Hold, Decay, Attack */
+#define ADC_ALC_HLDTIME_0_US 0x00
+#define ADC_ALC_HLDTIME_0000266_US 0x01 //time = 2.67ms
+#define ADC_ALC_HLDTIME_0000533_US 0x02 //time = 5.33ms
+#define ADC_ALC_HLDTIME_0001066_US 0x03 //time = 10.66ms
+#define ADC_ALC_HLDTIME_0002132_US 0x04 //time = 21.32ms
+#define ADC_ALC_HLDTIME_0004264_US 0x05 //time = 42.64ms
+#define ADC_ALC_HLDTIME_0008538_US 0x06 //time = 85.38ms
+#define ADC_ALC_HLDTIME_0017076_US 0x07 //time = 170.76ms
+#define ADC_ALC_HLDTIME_0034152_US 0x08 //time = 341.52ms
+#define ADC_ALC_HLDTIME_0680000_US 0x09 //time = 0.68s
+#define ADC_ALC_HLDTIME_1360000_US 0x0a //time = 1.36s
+
+#define ADC_ALC_DCYTIME_000410_US 0x00 //time = 410us
+#define ADC_ALC_DCYTIME_000820_US 0x01 //time = 820us
+#define ADC_ALC_DCYTIME_001640_US 0x02 //time = 1.64ms
+#define ADC_ALC_DCYTIME_003280_US 0x03 //time = 3.28ms
+#define ADC_ALC_DCYTIME_006560_US 0x04 //time = 6.56ms
+#define ADC_ALC_DCYTIME_013120_US 0x05 //time = 13.12ms
+#define ADC_ALC_DCYTIME_026240_US 0x06 //time = 26.24ms
+#define ADC_ALC_DCYTIME_058480_US 0x07 //time = 52.48ms
+#define ADC_ALC_DCYTIME_104960_US 0x08 //time = 104.96ms
+#define ADC_ALC_DCYTIME_209920_US 0x09 //time = 209.92ms
+#define ADC_ALC_DCYTIME_420000_US 0x0a //time = 420ms
+
+#define ADC_ALC_ATKTIME_000104_US 0x00 //time = 104us
+#define ADC_ALC_ATKTIME_000208_US 0x01 //time = 208us
+#define ADC_ALC_ATKTIME_000416_US 0x02 //time = 416ms
+#define ADC_ALC_ATKTIME_003832_US 0x03 //time = 832ms
+#define ADC_ALC_ATKTIME_001664_US 0x04 //time = 1.664ms
+#define ADC_ALC_ATKTIME_003328_US 0x05 //time = 3.328ms
+#define ADC_ALC_ATKTIME_006656_US 0x06 //time = 6.656ms
+#define ADC_ALC_ATKTIME_013312_US 0x07 //time = 13.312ms
+#define ADC_ALC_ATKTIME_026624_US 0x08 //time = 26.624ms
+#define ADC_ALC_ATKTIME_053248_US 0x09 //time = 53.248ms
+#define ADC_ALC_ATKTIME_106496_US 0x0a //time = 106.496ms
+
+/* ALC Noise Gate */
+#define ADC_ALC_NGTYPE_DISABLE 0x00 //noise gate disable
+#define ADC_ALC_NGTYPE_ENABLE_HOLD 0x01 //noise gate enable, hold gain type
+#define ADC_ALC_NGTYPE_ENABLE_MUTE 0x03 //noise gate enable, mute type
+
+#define ADC_ALC_NGTHLD_m76_5db 0x00 /* Threshold = -76.5db */
+#define ADC_ALC_NGTHLD_m75db 0x01 /* Threshold = -75db */
+#define ADC_ALC_NGTHLD_m73_5db 0x02 /* Threshold = -73.5db */
+#define ADC_ALC_NGTHLD_m72db 0x03 /* Threshold = -72db */
+#define ADC_ALC_NGTHLD_m70_5db 0x04 /* Threshold = -70.5db */
+#define ADC_ALC_NGTHLD_m69db 0x05 /* Threshold = -69db */
+#define ADC_ALC_NGTHLD_m67_5db 0x06 /* Threshold = -67.5db */
+#define ADC_ALC_NGTHLD_m66db 0x07 /* Threshold = -66db */
+#define ADC_ALC_NGTHLD_m64_5db 0x08 /* Threshold = -64.5db */
+#define ADC_ALC_NGTHLD_m63db 0x09 /* Threshold = -63db */
+#define ADC_ALC_NGTHLD_m61_5db 0x0a /* Threshold = -61.5db */
+#define ADC_ALC_NGTHLD_m60db 0x0b /* Threshold = -60db */
+#define ADC_ALC_NGTHLD_m58_5db 0x0c /* Threshold = -58.5db */
+#define ADC_ALC_NGTHLD_m57db 0x0d /* Threshold = -57db */
+#define ADC_ALC_NGTHLD_m55_5db 0x0e /* Threshold = -55.5db */
+#define ADC_ALC_NGTHLD_m54db 0x0f /* Threshold = -54db */
+#define ADC_ALC_NGTHLD_m52_5db 0x10 /* Threshold = -52.5db */
+#define ADC_ALC_NGTHLD_m51db 0x11 /* Threshold = -51db */
+#define ADC_ALC_NGTHLD_m49_5db 0x12 /* Threshold = -49.5db */
+#define ADC_ALC_NGTHLD_m48db 0x13 /* Threshold = -48db */
+#define ADC_ALC_NGTHLD_m46_5db 0x14 /* Threshold = -46.5db */
+#define ADC_ALC_NGTHLD_m45db 0x15 /* Threshold = -45db */
+#define ADC_ALC_NGTHLD_m43_5db 0x16 /* Threshold = -43.5db */
+#define ADC_ALC_NGTHLD_m42db 0x17 /* Threshold = -42db */
+#define ADC_ALC_NGTHLD_m40_5db 0x18 /* Threshold = -40.5db */
+#define ADC_ALC_NGTHLD_m39db 0x19 /* Threshold = -39db */
+#define ADC_ALC_NGTHLD_m37_5db 0x1a /* Threshold = -37.5db */
+#define ADC_ALC_NGTHLD_m36db 0x1b /* Threshold = -36db */
+#define ADC_ALC_NGTHLD_m34_5db 0x1c /* Threshold = -34.5db */
+#define ADC_ALC_NGTHLD_m33db 0x1d /* Threshold = -33db */
+#define ADC_ALC_NGTHLD_m31_5db 0x1e /* Threshold = -31.5db */
+#define ADC_ALC_NGTHLD_m30db 0x1f /* Threshold = -30db */
+
+/* Headphone dummy - Windows Specific flag, not needed for Linux */
+
+/* HPMIX HIGAIN and VOLUME */
+#define DAC_HPMIX_HIGAIN_0db 0x00 /* gain = 0db */
+#define DAC_HPMIX_HIGAIN_m6db 0x88 /* gain = -6db */
+
+#define DAC_HPMIX_VOLUME_m12db 0x00 /* volume = -12db */
+#define DAC_HPMIX_VOLUME_m10_5db 0x11 /* volume = -10.5db */
+#define DAC_HPMIX_VOLUME_m9db 0x22 /* volume = -9db */
+#define DAC_HPMIX_VOLUME_m7_5db 0x33 /* volume = -7.5db */
+#define DAC_HPMIX_VOLUME_m6db 0x44 /* volume = -6db */
+#define DAC_HPMIX_VOLUME_m4_5db 0x88 /* volume = -4.5db */
+#define DAC_HPMIX_VOLUME_m3db 0x99 /* volume = -3db */
+#define DAC_HPMIX_VOLUME_m1_5db 0xaa /* volume = -1.5db */
+#define DAC_HPMIX_VOLUME_0db 0xbb /* volume = 0db */
+
+/* HPOUT VOLUME */
+#define DAC_HPOUT_VOLUME_0db 0x00 /* volume = 0db */
+#define DAC_HPOUT_VOLUME_m12db 0x11 /* volume = -12db */
+#define DAC_HPOUT_VOLUME_m24db 0x22 /* volume = -24db */
+#define DAC_HPOUT_VOLUME_m48db 0x33 /* volume = -48db */
+
+/* LDAC/RDAC volume = 0db, -0.5db/setp, 0xc0 <-> -96db */
+
+/* Automute */
+#define DAC_AUTOMUTE_NONE 0x00 /* no automute */
+#define DAC_AUTOMUTE_DIGITAL 0x01 /* digital mute */
+#define DAC_AUTOMUTE_ANALOG 0x02 /* analog mute */
+
+/* Mono - Windows specific, on Linux the information comes from DAI/topology */
+#define HEADPHONE_MONO 0x01 /* on channel */
+#define HEADPHONE_STEREO 0x00 /* stereo */
+
+/* Speaker and headphone GPIO control */
+#define GPIO_CTL_IO_LEVEL_LOW 0x00 /* low level enable */
+#define GPIO_CTL_IO_LEVEL_HIGH 0x01 /* high level enable */
+
+/* GPIO */
+/* FIXME: for ES8396, no need to use */
+
+/* Platform clocks */
+/*
+ * BCLK AND MCLK FREQ
+ * BIT[7:4] MCLK FREQ
+ * 0 - 19.2MHz
+ * 1 - 24MHz
+ * 2 - 12.288MHz
+ * F - Default for 19.2MHz
+ *
+ * BIT[3:0] BCLK FREQ
+ * 0 - 4.8MHz
+ * 1 - 2.4MHz
+ * 2 - 2.304MHz
+ * 3 - 3.072MHz
+ * 4 - 4.096MHz
+ * F - Default for 4.8MHz
+ */
+
+int es83xx_dsm(struct device *dev, int arg, int *value);
+int es83xx_dsm_dump(struct device *dev);
+
+#endif
diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c
new file mode 100644
index 000000000000..6f57a3aeecc8
--- /dev/null
+++ b/sound/soc/codecs/framer-codec.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Framer ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/framer/framer.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define FRAMER_NB_CHANNEL 32
+#define FRAMER_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT)
+
+struct framer_codec {
+ struct framer *framer;
+ struct device *dev;
+ struct snd_soc_jack jack;
+ struct notifier_block nb;
+ struct work_struct carrier_work;
+ int max_chan_playback;
+ int max_chan_capture;
+};
+
+static int framer_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int width)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ 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;
+ }
+
+ framer->max_chan_playback = hweight32(tx_mask);
+ if (framer->max_chan_playback > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many tx slots defined (mask = 0x%x) supported max %d\n",
+ tx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ framer->max_chan_capture = hweight32(rx_mask);
+ if (framer->max_chan_capture > FRAMER_NB_CHANNEL) {
+ dev_err(dai->dev, "too many rx slots defined (mask = 0x%x) supported max %d\n",
+ rx_mask, FRAMER_NB_CHANNEL);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * The constraints for format/channel is to match with the number of 8bit
+ * time-slots available.
+ */
+static int framer_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ snd_pcm_format_t format = params_format(params);
+ struct snd_interval ch = {0};
+ int width;
+
+ width = snd_pcm_format_physical_width(format);
+ if (width == 8 || width == 16 || width == 32 || width == 64) {
+ ch.max = nb_ts * 8 / width;
+ } else {
+ dev_err(dai->dev, "format physical width %d not supported\n", width);
+ return -EINVAL;
+ }
+
+ ch.min = ch.max ? 1 : 0;
+
+ return snd_interval_refine(c, &ch);
+}
+
+static int framer_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_channels_by_format(dai, params, framer->max_chan_capture);
+}
+
+static int framer_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params,
+ unsigned int nb_ts)
+{
+ struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+ unsigned int channels = params_channels(params);
+ unsigned int slot_width;
+ snd_pcm_format_t format;
+ struct snd_mask f_new;
+
+ if (!channels || channels > nb_ts) {
+ dev_err(dai->dev, "channels %u not supported\n", nb_ts);
+ return -EINVAL;
+ }
+
+ slot_width = (nb_ts / channels) * 8;
+
+ snd_mask_none(&f_new);
+ pcm_for_each_format(format) {
+ if (snd_mask_test_format(f_old, format)) {
+ if (snd_pcm_format_physical_width(format) <= slot_width)
+ snd_mask_set_format(&f_new, format);
+ }
+ }
+
+ return snd_mask_refine(f_old, &f_new);
+}
+
+static int framer_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_playback);
+}
+
+static int framer_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_soc_dai *dai = rule->private;
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+
+ return framer_dai_hw_rule_format_by_channels(dai, params, framer->max_chan_capture);
+}
+
+static u64 framer_formats(u8 nb_ts)
+{
+ unsigned int format_width;
+ unsigned int chan_width;
+ snd_pcm_format_t format;
+ u64 formats_mask;
+
+ if (!nb_ts)
+ return 0;
+
+ formats_mask = 0;
+ chan_width = nb_ts * 8;
+ pcm_for_each_format(format) {
+ /* Support physical width multiple of 8bit */
+ format_width = snd_pcm_format_physical_width(format);
+ if (format_width == 0 || format_width % 8)
+ continue;
+
+ /*
+ * And support physical width that can fit N times in the
+ * channel
+ */
+ if (format_width > chan_width || chan_width % format_width)
+ continue;
+
+ formats_mask |= pcm_format_to_bits(format);
+ }
+ return formats_mask;
+}
+
+static int framer_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(dai->component);
+ snd_pcm_hw_rule_func_t hw_rule_channels_by_format;
+ snd_pcm_hw_rule_func_t hw_rule_format_by_channels;
+ unsigned int frame_bits;
+ u64 format;
+ int ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ format = framer_formats(framer->max_chan_capture);
+ hw_rule_channels_by_format = framer_dai_hw_rule_capture_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_capture_format_by_channels;
+ frame_bits = framer->max_chan_capture * 8;
+ } else {
+ format = framer_formats(framer->max_chan_playback);
+ hw_rule_channels_by_format = framer_dai_hw_rule_playback_channels_by_format;
+ hw_rule_format_by_channels = framer_dai_hw_rule_playback_format_by_channels;
+ frame_bits = framer->max_chan_playback * 8;
+ }
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT, format);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels_by_format, dai,
+ SNDRV_PCM_HW_PARAM_FORMAT, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT,
+ hw_rule_format_by_channels, dai,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add format rule (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FRAME_BITS,
+ frame_bits);
+ if (ret < 0) {
+ dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const u64 framer_dai_formats[] = {
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B,
+};
+
+static const struct snd_soc_dai_ops framer_dai_ops = {
+ .startup = framer_dai_startup,
+ .set_tdm_slot = framer_dai_set_tdm_slot,
+ .auto_selectable_formats = framer_dai_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
+};
+
+static struct snd_soc_dai_driver framer_dai_driver = {
+ .name = "framer",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = FRAMER_NB_CHANNEL,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = U64_MAX, /* Will be refined on DAI .startup() */
+ },
+ .ops = &framer_dai_ops,
+};
+
+static void framer_carrier_work(struct work_struct *work)
+{
+ struct framer_codec *framer = container_of(work, struct framer_codec, carrier_work);
+ struct framer_status framer_status;
+ int jack_status;
+ int ret;
+
+ ret = framer_get_status(framer->framer, &framer_status);
+ if (ret) {
+ dev_err(framer->dev, "get framer status failed (%d)\n", ret);
+ return;
+ }
+
+ jack_status = framer_status.link_is_on ? FRAMER_JACK_MASK : 0;
+ snd_soc_jack_report(&framer->jack, jack_status, FRAMER_JACK_MASK);
+}
+
+static int framer_carrier_notifier(struct notifier_block *nb, unsigned long action,
+ void *data)
+{
+ struct framer_codec *framer = container_of(nb, struct framer_codec, nb);
+
+ switch (action) {
+ case FRAMER_EVENT_STATUS:
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+ break;
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int framer_component_probe(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+ struct framer_status status;
+ char *name;
+ int ret;
+
+ INIT_WORK(&framer->carrier_work, framer_carrier_work);
+
+ name = "carrier";
+ if (component->name_prefix) {
+ name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix);
+ if (!name)
+ return -ENOMEM;
+ }
+
+ ret = snd_soc_card_jack_new(component->card, name, FRAMER_JACK_MASK, &framer->jack);
+ if (component->name_prefix)
+ kfree(name); /* A copy is done by snd_soc_card_jack_new */
+ if (ret) {
+ dev_err(component->dev, "Cannot create jack\n");
+ return ret;
+ }
+
+ ret = framer_init(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer init failed (%d)\n", ret);
+ return ret;
+ }
+
+ ret = framer_power_on(framer->framer);
+ if (ret) {
+ dev_err(component->dev, "framer power-on failed (%d)\n", ret);
+ goto framer_exit;
+ }
+
+ /* Be sure that get_status is supported */
+ ret = framer_get_status(framer->framer, &status);
+ if (ret) {
+ dev_err(component->dev, "get framer status failed (%d)\n", ret);
+ goto framer_power_off;
+ }
+
+ framer->nb.notifier_call = framer_carrier_notifier;
+ ret = framer_notifier_register(framer->framer, &framer->nb);
+ if (ret) {
+ dev_err(component->dev, "Cannot register event notifier\n");
+ goto framer_power_off;
+ }
+
+ /* Queue work to set the initial value */
+ queue_work(system_power_efficient_wq, &framer->carrier_work);
+
+ return 0;
+
+framer_power_off:
+ framer_power_off(framer->framer);
+framer_exit:
+ framer_exit(framer->framer);
+ return ret;
+}
+
+static void framer_component_remove(struct snd_soc_component *component)
+{
+ struct framer_codec *framer = snd_soc_component_get_drvdata(component);
+
+ framer_notifier_unregister(framer->framer, &framer->nb);
+ cancel_work_sync(&framer->carrier_work);
+ framer_power_off(framer->framer);
+ framer_exit(framer->framer);
+}
+
+static const struct snd_soc_component_driver framer_component_driver = {
+ .probe = framer_component_probe,
+ .remove = framer_component_remove,
+ .endianness = 1,
+};
+
+static int framer_codec_probe(struct platform_device *pdev)
+{
+ struct framer_codec *framer;
+
+ framer = devm_kzalloc(&pdev->dev, sizeof(*framer), GFP_KERNEL);
+ if (!framer)
+ return -ENOMEM;
+
+ framer->dev = &pdev->dev;
+
+ /* Get framer from parents node */
+ framer->framer = devm_framer_get(&pdev->dev, NULL);
+ if (IS_ERR(framer->framer))
+ return dev_err_probe(&pdev->dev, PTR_ERR(framer->framer), "get framer failed\n");
+
+ platform_set_drvdata(pdev, framer);
+
+ return devm_snd_soc_register_component(&pdev->dev, &framer_component_driver,
+ &framer_dai_driver, 1);
+}
+
+static struct platform_driver framer_codec_driver = {
+ .driver = {
+ .name = "framer-codec",
+ },
+ .probe = framer_codec_probe,
+};
+module_platform_driver(framer_codec_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("FRAMER ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.c b/sound/soc/codecs/fs-amp-lib.c
new file mode 100644
index 000000000000..c8f56617e370
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// fs-amp-lib.c --- Common library for FourSemi Audio Amplifiers
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/crc16.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fs-amp-lib.h"
+
+static int fs_get_scene_count(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ int count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ if (!table)
+ return -EFAULT;
+
+ count = table->size / sizeof(struct fs_scene_index);
+ if (count < 1 || count > FS_SCENE_COUNT_MAX) {
+ dev_err(amp_lib->dev, "Invalid scene count: %d\n", count);
+ return -ERANGE;
+ }
+
+ return count;
+}
+
+static void fs_get_fwm_string(struct fs_amp_lib *amp_lib,
+ int offset, const char **pstr)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !pstr)
+ return;
+
+ table = amp_lib->table[FS_INDEX_STRING];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ *pstr = (char *)table + offset;
+ else
+ *pstr = NULL;
+}
+
+static void fs_get_scene_reg(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_REG];
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->reg = (struct fs_reg_table *)((char *)table + offset);
+ else
+ scene->reg = NULL;
+}
+
+static void fs_get_scene_model(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_MODEL];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->model = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->model = NULL;
+}
+
+static void fs_get_scene_effect(struct fs_amp_lib *amp_lib,
+ int offset, struct fs_amp_scene *scene)
+{
+ const struct fs_fwm_table *table;
+ const char *ptr;
+
+ if (!amp_lib || !amp_lib->dev || !scene)
+ return;
+
+ table = amp_lib->table[FS_INDEX_EFFECT];
+ ptr = (char *)table;
+ if (table && offset > 0 && offset < table->size + sizeof(*table))
+ scene->effect = (struct fs_file_table *)(ptr + offset);
+ else
+ scene->effect = NULL;
+}
+
+static int fs_parse_scene_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_scene_index *scene_index;
+ const struct fs_fwm_table *table;
+ struct fs_amp_scene *scene;
+ int idx, count;
+
+ if (!amp_lib || !amp_lib->dev)
+ return -EINVAL;
+
+ count = fs_get_scene_count(amp_lib);
+ if (count <= 0)
+ return -EFAULT;
+
+ scene = devm_kcalloc(amp_lib->dev, count, sizeof(*scene), GFP_KERNEL);
+ if (!scene)
+ return -ENOMEM;
+
+ amp_lib->scene_count = count;
+ amp_lib->scene = scene;
+
+ table = amp_lib->table[FS_INDEX_SCENE];
+ scene_index = (struct fs_scene_index *)table->buf;
+
+ for (idx = 0; idx < count; idx++) {
+ fs_get_fwm_string(amp_lib, scene_index->name, &scene->name);
+ if (!scene->name)
+ scene->name = devm_kasprintf(amp_lib->dev,
+ GFP_KERNEL, "S%d", idx);
+ dev_dbg(amp_lib->dev, "scene.%d name: %s\n", idx, scene->name);
+ fs_get_scene_reg(amp_lib, scene_index->reg, scene);
+ fs_get_scene_model(amp_lib, scene_index->model, scene);
+ fs_get_scene_effect(amp_lib, scene_index->effect, scene);
+ scene++;
+ scene_index++;
+ }
+
+ return 0;
+}
+
+static int fs_parse_all_tables(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_table *table;
+ const struct fs_fwm_index *index;
+ const char *ptr;
+ int idx, count;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ /* Parse all fwm tables */
+ table = (struct fs_fwm_table *)amp_lib->hdr->params;
+ index = (struct fs_fwm_index *)table->buf;
+ count = table->size / sizeof(*index);
+
+ for (idx = 0; idx < count; idx++, index++) {
+ if (index->type >= FS_INDEX_MAX)
+ return -ERANGE;
+ ptr = (char *)table + (int)index->offset;
+ amp_lib->table[index->type] = (struct fs_fwm_table *)ptr;
+ }
+
+ /* Parse all scene tables */
+ ret = fs_parse_scene_tables(amp_lib);
+ if (ret)
+ dev_err(amp_lib->dev, "Failed to parse scene: %d\n", ret);
+
+ return ret;
+}
+
+static int fs_verify_firmware(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ int crcsum;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return -EINVAL;
+
+ hdr = amp_lib->hdr;
+
+ /* Verify the crcsum code */
+ crcsum = crc16(0x0000, (const char *)&hdr->crc_size, hdr->crc_size);
+ if (crcsum != hdr->crc16) {
+ dev_err(amp_lib->dev, "Failed to checksum: %x-%x\n",
+ crcsum, hdr->crc16);
+ return -EFAULT;
+ }
+
+ /* Verify the devid(chip_type) */
+ if (amp_lib->devid != LO_U16(hdr->chip_type)) {
+ dev_err(amp_lib->dev, "DEVID dismatch: %04X#%04X\n",
+ amp_lib->devid, hdr->chip_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void fs_print_firmware_info(struct fs_amp_lib *amp_lib)
+{
+ const struct fs_fwm_header *hdr;
+ const char *pro_name = NULL;
+ const char *dev_name = NULL;
+
+ if (!amp_lib || !amp_lib->dev || !amp_lib->hdr)
+ return;
+
+ hdr = amp_lib->hdr;
+
+ fs_get_fwm_string(amp_lib, hdr->project, &pro_name);
+ fs_get_fwm_string(amp_lib, hdr->device, &dev_name);
+
+ dev_info(amp_lib->dev, "Project: %s Device: %s\n",
+ pro_name ? pro_name : "null",
+ dev_name ? dev_name : "null");
+
+ dev_info(amp_lib->dev, "Date: %04d%02d%02d-%02d%02d\n",
+ hdr->date.year, hdr->date.month, hdr->date.day,
+ hdr->date.hour, hdr->date.minute);
+}
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name)
+{
+ const struct firmware *cont;
+ struct fs_fwm_header *hdr;
+ int ret;
+
+ if (!amp_lib || !amp_lib->dev || !name)
+ return -EINVAL;
+
+ ret = request_firmware(&cont, name, amp_lib->dev);
+ if (ret) {
+ dev_err(amp_lib->dev, "Failed to request %s: %d\n", name, ret);
+ return ret;
+ }
+
+ dev_info(amp_lib->dev, "Loading %s - size: %zu\n", name, cont->size);
+
+ hdr = devm_kmemdup(amp_lib->dev, cont->data, cont->size, GFP_KERNEL);
+ release_firmware(cont);
+ if (!hdr)
+ return -ENOMEM;
+
+ amp_lib->hdr = hdr;
+ ret = fs_verify_firmware(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ ret = fs_parse_all_tables(amp_lib);
+ if (ret) {
+ amp_lib->hdr = NULL;
+ return ret;
+ }
+
+ fs_print_firmware_info(amp_lib);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fs_amp_load_firmware);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FourSemi audio amplifier library");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs-amp-lib.h b/sound/soc/codecs/fs-amp-lib.h
new file mode 100644
index 000000000000..4a77c7b383cd
--- /dev/null
+++ b/sound/soc/codecs/fs-amp-lib.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * fs-amp-lib.h --- Common library for FourSemi Audio Amplifiers
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS_AMP_LIB_H__
+#define __FS_AMP_LIB_H__
+
+#define HI_U16(a) (((a) >> 8) & 0xFF)
+#define LO_U16(a) ((a) & 0xFF)
+#define FS_TABLE_NAME_LEN (4)
+#define FS_SCENE_COUNT_MAX (16)
+#define FS_CMD_DELAY_MS_MAX (100) /* 100ms */
+
+#define FS_CMD_DELAY (0xFF)
+#define FS_CMD_BURST (0xFE)
+#define FS_CMD_UPDATE (0xFD)
+
+#define FS_SOC_ENUM_EXT(xname, xhandler_info, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = xhandler_info, \
+ .get = xhandler_get, .put = xhandler_put \
+}
+
+enum fs_index_type {
+ FS_INDEX_INFO = 0,
+ FS_INDEX_STCOEF,
+ FS_INDEX_SCENE,
+ FS_INDEX_MODEL,
+ FS_INDEX_REG,
+ FS_INDEX_EFFECT,
+ FS_INDEX_STRING,
+ FS_INDEX_WOOFER,
+ FS_INDEX_MAX,
+};
+
+#pragma pack(push, 1)
+
+struct fs_reg_val {
+ u8 reg;
+ u16 val;
+};
+
+struct fs_reg_bits {
+ u8 cmd; /* FS_CMD_UPDATE */
+ u8 reg;
+ u16 val;
+ u16 mask;
+};
+
+struct fs_cmd_pkg {
+ union {
+ u8 cmd;
+ struct fs_reg_val regv;
+ struct fs_reg_bits regb;
+ };
+};
+
+struct fs_fwm_index {
+ /* Index type */
+ u16 type;
+ /* Offset address starting from the end of header */
+ u16 offset;
+};
+
+struct fs_fwm_table {
+ char name[FS_TABLE_NAME_LEN];
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_scene_index {
+ /* Offset address(scene name) in string table */
+ u16 name;
+ /* Offset address(scene reg) in register table */
+ u16 reg;
+ /* Offset address(scene model) in model table */
+ u16 model;
+ /* Offset address(scene effect) in effect table */
+ u16 effect;
+};
+
+struct fs_reg_table {
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_file_table {
+ u16 name;
+ u16 size; /* size of buf */
+ u8 buf[];
+};
+
+struct fs_fwm_date {
+ u32 year:12;
+ u32 month:4;
+ u32 day:5;
+ u32 hour:5;
+ u32 minute:6;
+};
+
+struct fs_fwm_header {
+ u16 version;
+ u16 project; /* Offset address(project name) in string table */
+ u16 device; /* Offset address(device name) in string table */
+ struct fs_fwm_date date;
+ u16 crc16;
+ u16 crc_size; /* Starting position for CRC checking */
+ u16 chip_type;
+ u16 addr; /* 7-bit i2c address */
+ u16 spkid;
+ u16 rsvd[6];
+ u8 params[];
+};
+
+#pragma pack(pop)
+
+struct fs_i2s_srate {
+ u32 srate; /* Sample rate */
+ u16 i2ssr; /* Value of Bit field[I2SSR] */
+};
+
+struct fs_pll_div {
+ unsigned int bclk; /* Rate of bit clock */
+ u16 pll1;
+ u16 pll2;
+ u16 pll3;
+};
+
+struct fs_amp_scene {
+ const char *name;
+ const struct fs_reg_table *reg;
+ const struct fs_file_table *model;
+ const struct fs_file_table *effect;
+};
+
+struct fs_amp_lib {
+ const struct fs_fwm_header *hdr;
+ const struct fs_fwm_table *table[FS_INDEX_MAX];
+ struct fs_amp_scene *scene;
+ struct device *dev;
+ int scene_count;
+ u16 devid;
+};
+
+int fs_amp_load_firmware(struct fs_amp_lib *amp_lib, const char *name);
+
+#endif // __FS_AMP_LIB_H__
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
new file mode 100644
index 000000000000..e2f85714972d
--- /dev/null
+++ b/sound/soc/codecs/fs210x.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// fs210x.c -- Driver for the FS2104/5S Audio Amplifier
+//
+// Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "fs210x.h"
+#include "fs-amp-lib.h"
+
+#define FS210X_DEFAULT_FWM_NAME "fs210x_fwm.bin"
+#define FS210X_DEFAULT_DAI_NAME "fs210x-aif"
+#define FS2105S_DEVICE_ID 0x20 /* FS2105S */
+#define FS210X_DEVICE_ID 0x45 /* FS2104 */
+#define FS210X_REG_MAX 0xF8
+#define FS210X_INIT_SCENE 0
+#define FS210X_DEFAULT_SCENE 1
+#define FS210X_START_DELAY_MS 5
+#define FS210X_FAULT_CHECK_INTERVAL_MS 2000
+#define FS2105S_RATES (SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
+#define FS210X_RATES (SNDRV_PCM_RATE_16000 | FS2105S_RATES)
+#define FS210X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define FS210X_NUM_SUPPLIES ARRAY_SIZE(fs210x_supply_names)
+
+static const char *const fs210x_supply_names[] = {
+ "pvdd",
+ "dvdd",
+};
+
+struct fs210x_platform_data {
+ const char *fwm_name;
+};
+
+struct fs210x_priv {
+ struct i2c_client *i2c;
+ struct device *dev;
+ struct regmap *regmap;
+ struct fs210x_platform_data pdata;
+ struct regulator_bulk_data supplies[FS210X_NUM_SUPPLIES];
+ struct gpio_desc *gpio_sdz;
+ struct delayed_work start_work;
+ struct delayed_work fault_check_work;
+ struct fs_amp_lib amp_lib;
+ const struct fs_amp_scene *cur_scene;
+ struct clk *clk_bclk;
+ /*
+ * @lock: Mutex ensuring exclusive access for critical device operations
+ *
+ * This lock serializes access between the following actions:
+ * - Device initialization procedures(probe)
+ * - Enable/disable device(DAPM event)
+ * - Suspend/resume device(PM)
+ * - Runtime scene switching(control)
+ * - Scheduling/execution of delayed works items(delayed works)
+ */
+ struct mutex lock;
+ unsigned int check_interval_ms;
+ unsigned int bclk;
+ unsigned int srate;
+ int scene_id;
+ u16 devid;
+ bool is_inited;
+ bool is_suspended;
+ bool is_bclk_on;
+ bool is_playing;
+};
+
+static const unsigned int fs2105s_rates[] = {
+ 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs2105s_constraints = {
+ .count = ARRAY_SIZE(fs2105s_rates),
+ .list = fs2105s_rates,
+};
+
+static const unsigned int fs210x_rates[] = {
+ 16000, 32000, 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list fs210x_constraints = {
+ .count = ARRAY_SIZE(fs210x_rates),
+ .list = fs210x_rates,
+};
+
+static const struct fs_pll_div fs210x_pll_div[] = {
+ /* bclk, pll1, pll2, pll3 */
+ { 512000, 0x006C, 0x0120, 0x0001 },
+ { 768000, 0x016C, 0x00C0, 0x0001 },
+ { 1024000, 0x016C, 0x0090, 0x0001 },
+ { 1536000, 0x016C, 0x0060, 0x0001 },
+ { 2048000, 0x016C, 0x0090, 0x0002 },
+ { 2304000, 0x016C, 0x0080, 0x0002 },
+ { 3072000, 0x016C, 0x0090, 0x0003 },
+ { 4096000, 0x016C, 0x0090, 0x0004 },
+ { 4608000, 0x016C, 0x0080, 0x0004 },
+ { 6144000, 0x016C, 0x0090, 0x0006 },
+ { 8192000, 0x016C, 0x0090, 0x0008 },
+ { 9216000, 0x016C, 0x0090, 0x0009 },
+ { 12288000, 0x016C, 0x0090, 0x000C },
+ { 16384000, 0x016C, 0x0090, 0x0010 },
+ { 18432000, 0x016C, 0x0090, 0x0012 },
+ { 24576000, 0x016C, 0x0090, 0x0018 },
+ { 1411200, 0x016C, 0x0060, 0x0001 },
+ { 2116800, 0x016C, 0x0080, 0x0002 },
+ { 2822400, 0x016C, 0x0090, 0x0003 },
+ { 4233600, 0x016C, 0x0080, 0x0004 },
+ { 5644800, 0x016C, 0x0090, 0x0006 },
+ { 8467200, 0x016C, 0x0090, 0x0009 },
+ { 11289600, 0x016C, 0x0090, 0x000C },
+ { 16934400, 0x016C, 0x0090, 0x0012 },
+ { 22579200, 0x016C, 0x0090, 0x0018 },
+ { 2000000, 0x017C, 0x0093, 0x0002 },
+};
+
+static int fs210x_bclk_set(struct fs210x_priv *fs210x, bool on)
+{
+ int ret = 0;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if ((fs210x->is_bclk_on ^ on) == 0)
+ return 0;
+
+ if (on) {
+ clk_set_rate(fs210x->clk_bclk, fs210x->bclk);
+ ret = clk_prepare_enable(fs210x->clk_bclk);
+ fs210x->is_bclk_on = true;
+ fsleep(2000); /* >= 2ms */
+ } else {
+ clk_disable_unprepare(fs210x->clk_bclk);
+ fs210x->is_bclk_on = false;
+ }
+
+ return ret;
+}
+
+static int fs210x_reg_write(struct fs210x_priv *fs210x,
+ u8 reg, u16 val)
+{
+ int ret;
+
+ ret = regmap_write(fs210x->regmap, reg, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_read(struct fs210x_priv *fs210x,
+ u8 reg, u16 *pval)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(fs210x->regmap, reg, &val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to read %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ *pval = (u16)val;
+
+ return 0;
+}
+
+static int fs210x_reg_update_bits(struct fs210x_priv *fs210x,
+ u8 reg, u16 mask, u16 val)
+{
+ int ret;
+
+ ret = regmap_update_bits(fs210x->regmap, reg, mask, val);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to update %02Xh: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_reg_bulk_write(struct fs210x_priv *fs210x,
+ u8 reg, const void *val, u32 size)
+{
+ int ret;
+
+ ret = regmap_bulk_write(fs210x->regmap, reg, val, size / 2);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to bulk write %02Xh: %d\n",
+ reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int fs210x_write_reg_val(struct fs210x_priv *fs210x,
+ const struct fs_reg_val *regv)
+{
+ return fs210x_reg_write(fs210x, regv->reg, regv->val);
+}
+
+static inline int fs210x_write_reg_bits(struct fs210x_priv *fs210x,
+ const struct fs_reg_bits *regu)
+{
+ return fs210x_reg_update_bits(fs210x,
+ regu->reg,
+ regu->mask,
+ regu->val);
+}
+
+static inline int fs210x_set_cmd_pkg(struct fs210x_priv *fs210x,
+ const struct fs_cmd_pkg *pkg,
+ unsigned int *offset)
+{
+ int delay_us;
+
+ if (pkg->cmd >= 0x00 && pkg->cmd <= FS210X_REG_MAX) {
+ *offset = sizeof(pkg->regv);
+ return fs210x_write_reg_val(fs210x, &pkg->regv);
+ } else if (pkg->cmd == FS_CMD_UPDATE) {
+ *offset = sizeof(pkg->regb);
+ return fs210x_write_reg_bits(fs210x, &pkg->regb);
+ } else if (pkg->cmd == FS_CMD_DELAY) {
+ if (pkg->regv.val > FS_CMD_DELAY_MS_MAX)
+ return -EOPNOTSUPP;
+ delay_us = pkg->regv.val * 1000; /* ms -> us */
+ fsleep(delay_us);
+ *offset = sizeof(pkg->regv);
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "Invalid pkg cmd: %d\n", pkg->cmd);
+
+ return -EOPNOTSUPP;
+}
+
+static int fs210x_reg_write_table(struct fs210x_priv *fs210x,
+ const struct fs_reg_table *reg)
+{
+ const struct fs_cmd_pkg *pkg;
+ unsigned int index, offset;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (!reg || reg->size == 0)
+ return -EFAULT;
+
+ for (index = 0; index < reg->size; index += offset) {
+ pkg = (struct fs_cmd_pkg *)(reg->buf + index);
+ ret = fs210x_set_cmd_pkg(fs210x, pkg, &offset);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set cmd pkg: %02X-%d\n",
+ pkg->cmd, ret);
+ return ret;
+ }
+ }
+
+ if (index != reg->size) {
+ dev_err(fs210x->dev, "Invalid reg table size: %d-%d\n",
+ index, reg->size);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int fs210x_dev_play(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PLAY);
+ if (!ret)
+ fs210x->is_playing = true;
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_dev_stop(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ if (!fs210x->is_inited)
+ return -EFAULT;
+
+ if (!fs210x->is_playing)
+ return 0;
+
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fs210x->is_playing = false;
+
+ fsleep(30000); /* >= 30ms */
+
+ return ret;
+}
+
+static int fs210x_set_reg_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_reg_table *reg;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->reg || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing reg table\n");
+ return 0;
+ }
+
+ reg = scene->reg;
+ dev_dbg(fs210x->dev, "reg table size: %d\n", reg->size);
+
+ return fs210x_reg_write_table(fs210x, reg);
+}
+
+static int fs210x_set_woofer_table(struct fs210x_priv *fs210x)
+{
+ const struct fs_file_table *woofer;
+ const struct fs_fwm_table *table;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ /* NOTE: fs2105s has woofer ram only */
+ if (fs210x->devid != FS2105S_DEVICE_ID)
+ return 0;
+
+ table = fs210x->amp_lib.table[FS_INDEX_WOOFER];
+ if (!table) {
+ dev_dbg(fs210x->dev, "Skip writing woofer table\n");
+ return 0;
+ }
+
+ woofer = (struct fs_file_table *)table->buf;
+ dev_dbg(fs210x->dev, "woofer table size: %d\n", woofer->size);
+ /* Unit of woofer data is u32(4 bytes) */
+ if (woofer->size == 0 || (woofer->size & 0x3)) {
+ dev_err(fs210x->dev, "Invalid woofer size: %d\n",
+ woofer->size);
+ return -EINVAL;
+ }
+
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS2105S_46H_CAM_BURST_W);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ woofer->buf, woofer->size);
+
+ return ret;
+}
+
+static int fs210x_set_effect_table(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene)
+{
+ const struct fs_amp_scene *cur_scene;
+ const struct fs_file_table *effect;
+ int half_size;
+ int ret;
+
+ if (!fs210x || !fs210x->dev || !scene)
+ return -EINVAL;
+
+ cur_scene = fs210x->cur_scene;
+ if (!scene->effect || cur_scene == scene) {
+ dev_dbg(fs210x->dev, "Skip writing effect table\n");
+ return 0;
+ }
+
+ effect = scene->effect;
+ dev_dbg(fs210x->dev, "effect table size: %d\n", effect->size);
+
+ /* Unit of effect data is u32(4 bytes), 2 channels */
+ if (effect->size == 0 || (effect->size & 0x7)) {
+ dev_err(fs210x->dev, "Invalid effect size: %d\n",
+ effect->size);
+ return -EINVAL;
+ }
+
+ half_size = effect->size / 2;
+
+ /* Left channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_L);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf, half_size);
+ if (ret)
+ return ret;
+
+ /* Right channel */
+ ret = fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_BURST_R);
+ ret |= fs210x_reg_bulk_write(fs210x, FS210X_42H_DACEQWL,
+ effect->buf + half_size, half_size);
+
+ return ret;
+}
+
+static int fs210x_access_dsp_ram(struct fs210x_priv *fs210x, bool enable)
+{
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ if (enable) {
+ ret = fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_HIZ);
+ ret |= fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_ON);
+ } else {
+ ret = fs210x_reg_write(fs210x, FS210X_0BH_ACCKEY,
+ FS210X_0BH_ACCKEY_OFF);
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ return ret;
+}
+
+static int fs210x_write_dsp_effect(struct fs210x_priv *fs210x,
+ const struct fs_amp_scene *scene,
+ int scene_id)
+{
+ int ret;
+
+ if (!fs210x || !scene)
+ return -EINVAL;
+
+ ret = fs210x_access_dsp_ram(fs210x, true);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to access dsp: %d\n", ret);
+ goto tag_exit;
+ }
+
+ ret = fs210x_set_effect_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set effect: %d\n", ret);
+ goto tag_exit;
+ }
+
+ if (scene_id == FS210X_INIT_SCENE)
+ ret = fs210x_set_woofer_table(fs210x);
+
+tag_exit:
+ fs210x_reg_write(fs210x, FS210X_46H_DACEQA,
+ FS210X_46H_CAM_CLEAR);
+ fs210x_access_dsp_ram(fs210x, false);
+
+ return ret;
+}
+
+static int fs210x_check_scene(struct fs210x_priv *fs210x,
+ int scene_id, bool *skip_set)
+{
+ struct fs_amp_lib *amp_lib;
+
+ if (!fs210x || !skip_set)
+ return -EINVAL;
+
+ amp_lib = &fs210x->amp_lib;
+ if (amp_lib->scene_count == 0 || !amp_lib->scene) {
+ dev_err(fs210x->dev, "There's no scene data\n");
+ return -EINVAL;
+ }
+
+ if (scene_id < 0 || scene_id >= amp_lib->scene_count) {
+ dev_err(fs210x->dev, "Invalid scene_id: %d\n", scene_id);
+ return -EINVAL;
+ }
+
+ if (fs210x->scene_id == scene_id) {
+ dev_dbg(fs210x->dev, "Skip to set same scene\n");
+ return 0;
+ }
+
+ *skip_set = false;
+
+ return 0;
+}
+
+static int fs210x_set_scene(struct fs210x_priv *fs210x, int scene_id)
+{
+ const struct fs_amp_scene *scene;
+ bool skip_set = true;
+ bool is_playing;
+ int ret;
+
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = fs210x_check_scene(fs210x, scene_id, &skip_set);
+ if (ret || skip_set)
+ return ret;
+
+ scene = fs210x->amp_lib.scene + scene_id;
+ dev_info(fs210x->dev, "Switch scene.%d: %s\n",
+ scene_id, scene->name);
+
+ is_playing = fs210x->is_playing;
+ if (is_playing)
+ fs210x_dev_stop(fs210x);
+
+ ret = fs210x_set_reg_table(fs210x, scene);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = fs210x_write_dsp_effect(fs210x, scene, scene_id);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to write ram: %d\n", ret);
+ return ret;
+ }
+
+ fs210x->cur_scene = scene;
+ fs210x->scene_id = scene_id;
+
+ if (is_playing)
+ fs210x_dev_play(fs210x);
+
+ return 0;
+}
+
+static int fs210x_init_chip(struct fs210x_priv *fs210x)
+{
+ int scene_id;
+ int ret;
+
+ regcache_cache_bypass(fs210x->regmap, true);
+
+ if (!fs210x->gpio_sdz) {
+ /* Gpio is not found, i2c reset */
+ ret = fs210x_reg_write(fs210x, FS210X_10H_PWRCTRL,
+ FS210X_10H_I2C_RESET);
+ if (ret)
+ goto tag_power_down;
+ } else {
+ /* gpio reset, deactivate */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0);
+ }
+
+ fsleep(10000); /* >= 10ms */
+
+ /* Backup scene id */
+ scene_id = fs210x->scene_id;
+ fs210x->scene_id = -1;
+
+ /* Init registers/RAM by init scene */
+ ret = fs210x_set_scene(fs210x, FS210X_INIT_SCENE);
+ if (ret)
+ goto tag_power_down;
+
+ /*
+ * If the firmware has effect scene(s),
+ * we load effect scene by default scene or scene_id
+ */
+ if (fs210x->amp_lib.scene_count > 1) {
+ if (scene_id < FS210X_DEFAULT_SCENE)
+ scene_id = FS210X_DEFAULT_SCENE;
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ goto tag_power_down;
+ }
+
+tag_power_down:
+ /* Power down the device */
+ ret |= fs210x_reg_write(fs210x, FS210X_11H_SYSCTRL,
+ FS210X_11H_DPS_PWDN);
+ fsleep(10000); /* >= 10ms */
+
+ regcache_cache_bypass(fs210x->regmap, false);
+ if (!ret) {
+ regcache_mark_dirty(fs210x->regmap);
+ regcache_sync(fs210x->regmap);
+ fs210x->is_inited = true;
+ }
+
+ return ret;
+}
+
+static int fs210x_set_i2s_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_i2s_srate params[] = {
+ { 16000, 0x3 },
+ { 32000, 0x7 },
+ { 44100, 0x8 },
+ { 48000, 0x9 },
+ { 88200, 0xA },
+ { 96000, 0xB },
+ };
+ u16 val;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(params); i++) {
+ if (params[i].srate != fs210x->srate)
+ continue;
+ val = params[i].i2ssr << FS210X_17H_I2SSR_SHIFT;
+ ret = fs210x_reg_update_bits(fs210x,
+ FS210X_17H_I2SCTRL,
+ FS210X_17H_I2SSR_MASK,
+ val);
+ return ret;
+ }
+
+ dev_err(fs210x->dev, "Invalid sample rate: %d\n", fs210x->srate);
+
+ return -EINVAL;
+}
+
+static int fs210x_get_pll_div(struct fs210x_priv *fs210x,
+ const struct fs_pll_div **pll_div)
+{
+ int i;
+
+ if (!fs210x || !pll_div)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(fs210x_pll_div); i++) {
+ if (fs210x_pll_div[i].bclk != fs210x->bclk)
+ continue;
+ *pll_div = fs210x_pll_div + i;
+ return 0;
+ }
+
+ dev_err(fs210x->dev, "No PLL table for bclk: %d\n", fs210x->bclk);
+
+ return -EFAULT;
+}
+
+static int fs210x_set_hw_params(struct fs210x_priv *fs210x)
+{
+ const struct fs_pll_div *pll_div;
+ int ret;
+
+ ret = fs210x_set_i2s_params(fs210x);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to set i2s params: %d\n", ret);
+ return ret;
+ }
+
+ /* Set pll params */
+ ret = fs210x_get_pll_div(fs210x, &pll_div);
+ if (ret)
+ return ret;
+
+ ret = fs210x_reg_write(fs210x, FS210X_A1H_PLLCTRL1, pll_div->pll1);
+ ret |= fs210x_reg_write(fs210x, FS210X_A2H_PLLCTRL2, pll_div->pll2);
+ ret |= fs210x_reg_write(fs210x, FS210X_A3H_PLLCTRL3, pll_div->pll3);
+
+ return ret;
+}
+
+static int fs210x_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ const struct snd_pcm_hw_constraint_list *list;
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+ if (!fs210x) {
+ pr_err("dai_startup: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ if (!substream->runtime)
+ return 0;
+
+ ret = snd_pcm_hw_constraint_mask64(substream->runtime,
+ SNDRV_PCM_HW_PARAM_FORMAT,
+ FS210X_FORMATS);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param format: %d\n", ret);
+ return ret;
+ }
+
+ if (fs210x->devid == FS2105S_DEVICE_ID)
+ list = &fs2105s_constraints;
+ else
+ list = &fs210x_constraints;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ list);
+ if (ret < 0) {
+ dev_err(fs210x->dev,
+ "Failed to set hw param rate: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ /* Only supports consumer mode */
+ break;
+ default:
+ dev_err(fs210x->dev, "Only supports consumer mode\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+ int chn_num;
+ int ret;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ fs210x->srate = params_rate(params);
+ fs210x->bclk = snd_soc_params_to_bclk(params);
+ chn_num = params_channels(params);
+ if (chn_num == 1) /* mono */
+ fs210x->bclk *= 2; /* I2S bus has 2 channels */
+
+ /* The FS2105S can't support 16kHz sample rate. */
+ if (fs210x->devid == FS2105S_DEVICE_ID && fs210x->srate == 16000)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_set_hw_params(fs210x);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set hw params: %d\n", ret);
+
+ return ret;
+}
+
+static int fs210x_dai_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct fs210x_priv *fs210x;
+ unsigned long delay;
+
+ if (stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return 0;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ if (mute) {
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+ cancel_delayed_work_sync(&fs210x->start_work);
+ } else {
+ delay = msecs_to_jiffies(fs210x->check_interval_ms);
+ schedule_delayed_work(&fs210x->fault_check_work, delay);
+ }
+
+ return 0;
+}
+
+static int fs210x_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(dai->component);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ /*
+ * According to the power up/down sequence of FS210x,
+ * it requests the I2S clock has been present
+ * and stable(>= 2ms) before playing.
+ */
+ schedule_delayed_work(&fs210x->start_work,
+ msecs_to_jiffies(FS210X_START_DELAY_MS));
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void fs210x_start_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, start_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ ret = fs210x_dev_play(fs210x);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to start playing: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+}
+
+static void fs210x_fault_check_work(struct work_struct *work)
+{
+ struct fs210x_priv *fs210x;
+ u16 status;
+ int ret;
+
+ fs210x = container_of(work, struct fs210x_priv, fault_check_work.work);
+
+ mutex_lock(&fs210x->lock);
+
+ if (!fs210x->is_inited || fs210x->is_suspended || !fs210x->is_playing) {
+ mutex_unlock(&fs210x->lock);
+ return;
+ }
+
+ ret = fs210x_reg_read(fs210x, FS210X_05H_ANASTAT, &status);
+ mutex_unlock(&fs210x->lock);
+ if (ret)
+ return;
+
+ if (!(status & FS210X_05H_PVDD_MASK))
+ dev_err(fs210x->dev, "PVDD fault\n");
+ if (status & FS210X_05H_OCDL_MASK)
+ dev_err(fs210x->dev, "OC detected\n");
+ if (status & FS210X_05H_UVDL_MASK)
+ dev_err(fs210x->dev, "UV detected\n");
+ if (status & FS210X_05H_OVDL_MASK)
+ dev_err(fs210x->dev, "OV detected\n");
+ if (status & FS210X_05H_OTPDL_MASK)
+ dev_err(fs210x->dev, "OT detected\n");
+ if (status & FS210X_05H_OCRDL_MASK)
+ dev_err(fs210x->dev, "OCR detected\n");
+ if (status & FS210X_05H_OCLDL_MASK)
+ dev_err(fs210x->dev, "OCL detected\n");
+ if (status & FS210X_05H_DCRDL_MASK)
+ dev_err(fs210x->dev, "DCR detected\n");
+ if (status & FS210X_05H_DCLDL_MASK)
+ dev_err(fs210x->dev, "DCL detected\n");
+ if (status & FS210X_05H_SRDL_MASK)
+ dev_err(fs210x->dev, "SR detected\n");
+ if (status & FS210X_05H_OTWDL_MASK)
+ dev_err(fs210x->dev, "OTW detected\n");
+ if (!(status & FS210X_05H_AMPS_MASK))
+ dev_dbg(fs210x->dev, "Amplifier unready\n");
+ if (!(status & FS210X_05H_PLLS_MASK))
+ dev_err(fs210x->dev, "PLL unlock\n");
+ if (!(status & FS210X_05H_ANAS_MASK))
+ dev_err(fs210x->dev, "Analog power fault\n");
+
+ schedule_delayed_work(&fs210x->fault_check_work,
+ msecs_to_jiffies(fs210x->check_interval_ms));
+}
+
+static int fs210x_get_drvdata_from_kctrl(struct snd_kcontrol *kctrl,
+ struct fs210x_priv **fs210x)
+{
+ struct snd_soc_component *cmpnt;
+
+ if (!kctrl) {
+ pr_err("fs210x: kcontrol is null\n");
+ return -EINVAL;
+ }
+
+ cmpnt = snd_soc_kcontrol_component(kctrl);
+ if (!cmpnt) {
+ pr_err("fs210x: component is null\n");
+ return -EINVAL;
+ }
+
+ *fs210x = snd_soc_component_get_drvdata(cmpnt);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ const struct fs_amp_scene *scene;
+ struct fs210x_priv *fs210x;
+ const char *name = "N/A";
+ int idx, count;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_info: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (count < 1) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ idx = uinfo->value.enumerated.item;
+ scene = fs210x->amp_lib.scene + idx + 1;
+ if (scene->name)
+ name = scene->name;
+
+ strscpy(uinfo->value.enumerated.name, name, strlen(name) + 1);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int index;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_get: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ /* The id of effect scene is from 1 to N. */
+ if (fs210x->scene_id < 1)
+ return -EINVAL;
+
+ mutex_lock(&fs210x->lock);
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0
+ * effect scene(s): id = 1~N (optional)
+ * effect_index = scene_id - 1
+ */
+ index = fs210x->scene_id - 1;
+ ucontrol->value.integer.value[0] = index;
+ mutex_unlock(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_effect_scene_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct fs210x_priv *fs210x;
+ int scene_id, scene_count;
+ bool is_changed = false;
+ int ret;
+
+ ret = fs210x_get_drvdata_from_kctrl(kcontrol, &fs210x);
+ if (ret || !fs210x->dev) {
+ pr_err("scene_effect_put: fs210x is null\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ /*
+ * FS210x has scene(s) as below:
+ * init scene: id = 0 (It's set in fs210x_init_chip() only)
+ * effect scene(s): id = 1~N (optional)
+ * scene_id = effect_index + 1.
+ */
+ scene_id = ucontrol->value.integer.value[0] + 1;
+ scene_count = fs210x->amp_lib.scene_count - 1; /* Skip init scene */
+ if (scene_id < 1 || scene_id > scene_count) {
+ mutex_unlock(&fs210x->lock);
+ return -ERANGE;
+ }
+
+ if (scene_id != fs210x->scene_id)
+ is_changed = true;
+
+ if (fs210x->is_suspended) {
+ fs210x->scene_id = scene_id;
+ mutex_unlock(&fs210x->lock);
+ return is_changed;
+ }
+
+ ret = fs210x_set_scene(fs210x, scene_id);
+ if (ret)
+ dev_err(fs210x->dev, "Failed to set scene: %d\n", ret);
+
+ mutex_unlock(&fs210x->lock);
+
+ if (!ret && is_changed)
+ return 1;
+
+ return ret;
+}
+
+static int fs210x_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kc, int event)
+{
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct fs210x_priv *fs210x = snd_soc_component_get_drvdata(cmpnt);
+ int ret = 0;
+
+ mutex_lock(&fs210x->lock);
+
+ if (fs210x->is_suspended) {
+ mutex_unlock(&fs210x->lock);
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /*
+ * If there is no bclk for us to set the clock output,
+ * we will enable the device(start_work) in dai trigger.
+ */
+ if (!fs210x->clk_bclk)
+ break;
+ fs210x_bclk_set(fs210x, true);
+ ret = fs210x_dev_play(fs210x);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = fs210x_dev_stop(fs210x);
+ fs210x_bclk_set(fs210x, false);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops fs210x_dai_ops = {
+ .startup = fs210x_dai_startup,
+ .set_fmt = fs210x_dai_set_fmt,
+ .hw_params = fs210x_dai_hw_params,
+ .mute_stream = fs210x_dai_mute,
+ .trigger = fs210x_dai_trigger,
+};
+
+static const struct snd_soc_dai_driver fs210x_dai = {
+ .name = FS210X_DEFAULT_DAI_NAME,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = FS210X_RATES,
+ .formats = FS210X_FORMATS,
+ },
+ .ops = &fs210x_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(fs2105s_vol_tlv, -9709, 19, 1);
+static const DECLARE_TLV_DB_SCALE(fs210x_vol_tlv, -13357, 19, 1);
+
+static const struct snd_kcontrol_new fs2105s_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 7, 0x1FF, 0, fs2105s_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_vol_control[] = {
+ SOC_DOUBLE_R_TLV("PCM Playback Volume",
+ FS210X_39H_LVOLCTRL, FS210X_3AH_RVOLCTRL,
+ 6, 0x2BF, 0, fs210x_vol_tlv),
+};
+
+static const struct snd_kcontrol_new fs210x_controls[] = {
+ SOC_DOUBLE("DAC Mute Switch", FS210X_30H_DACCTRL, 4, 8, 1, 0),
+ SOC_DOUBLE("DAC Fade Switch", FS210X_30H_DACCTRL, 5, 9, 1, 0),
+};
+
+static const struct snd_kcontrol_new fs210x_scene_control[] = {
+ FS_SOC_ENUM_EXT("Effect Scene",
+ fs210x_effect_scene_info,
+ fs210x_effect_scene_get,
+ fs210x_effect_scene_put),
+};
+
+static const struct snd_soc_dapm_widget fs210x_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("AIF IN", "Playback", 0, SND_SOC_NOPM, 0, 0,
+ fs210x_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("AIF OUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_INPUT("SDO"),
+};
+
+static const struct snd_soc_dapm_route fs210x_dapm_routes[] = {
+ { "OUTL", NULL, "AIF IN" },
+ { "OUTR", NULL, "AIF IN" },
+ { "AIF OUT", NULL, "SDO" },
+};
+
+static int fs210x_add_mixer_controls(struct fs210x_priv *fs210x,
+ struct snd_soc_component *cmpnt)
+{
+ const struct snd_kcontrol_new *kctrl;
+ int count;
+ int ret;
+
+ if (!fs210x || !cmpnt)
+ return -EINVAL;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ kctrl = fs2105s_vol_control;
+ count = ARRAY_SIZE(fs2105s_vol_control);
+ } else {
+ kctrl = fs210x_vol_control;
+ count = ARRAY_SIZE(fs210x_vol_control);
+ }
+
+ ret = snd_soc_add_component_controls(cmpnt, kctrl, count);
+ if (ret)
+ return ret;
+
+ /*
+ * If the firmware has no scene or only init scene,
+ * we skip adding this mixer control.
+ */
+ if (fs210x->amp_lib.scene_count < 2)
+ return 0;
+
+ kctrl = fs210x_scene_control;
+ count = ARRAY_SIZE(fs210x_scene_control);
+
+ return snd_soc_add_component_controls(cmpnt, kctrl, count);
+}
+
+static int fs210x_probe(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ fs210x->amp_lib.dev = fs210x->dev;
+ fs210x->amp_lib.devid = fs210x->devid;
+
+ ret = fs_amp_load_firmware(&fs210x->amp_lib, fs210x->pdata.fwm_name);
+ if (ret)
+ return ret;
+
+ ret = fs210x_add_mixer_controls(fs210x, cmpnt);
+ if (ret)
+ return ret;
+
+ mutex_lock(&fs210x->lock);
+ ret = fs210x_init_chip(fs210x);
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+
+static void fs210x_remove(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return;
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+}
+
+#ifdef CONFIG_PM
+static int fs210x_suspend(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ regcache_cache_only(fs210x->regmap, true);
+
+ mutex_lock(&fs210x->lock);
+ fs210x->cur_scene = NULL;
+ fs210x->is_inited = false;
+ fs210x->is_playing = false;
+ fs210x->is_suspended = true;
+
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(30000); /* >= 30ms */
+ mutex_unlock(&fs210x->lock);
+
+ cancel_delayed_work_sync(&fs210x->start_work);
+ cancel_delayed_work_sync(&fs210x->fault_check_work);
+
+ ret = regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to suspend: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fs210x_resume(struct snd_soc_component *cmpnt)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = snd_soc_component_get_drvdata(cmpnt);
+ if (!fs210x || !fs210x->dev)
+ return -EINVAL;
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret) {
+ dev_err(fs210x->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&fs210x->lock);
+
+ fs210x->is_suspended = false;
+ ret = fs210x_init_chip(fs210x);
+
+ mutex_unlock(&fs210x->lock);
+
+ return ret;
+}
+#else
+#define fs210x_suspend NULL
+#define fs210x_resume NULL
+#endif // CONFIG_PM
+
+static bool fs210x_volatile_registers(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case FS210X_00H_STATUS ... FS210X_0FH_I2CADDR:
+ case FS210X_ABH_INTSTAT:
+ case FS210X_ACH_INTSTATR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct snd_soc_component_driver fs210x_soc_component_dev = {
+ .probe = fs210x_probe,
+ .remove = fs210x_remove,
+ .suspend = fs210x_suspend,
+ .resume = fs210x_resume,
+ .controls = fs210x_controls,
+ .num_controls = ARRAY_SIZE(fs210x_controls),
+ .dapm_widgets = fs210x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(fs210x_dapm_widgets),
+ .dapm_routes = fs210x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(fs210x_dapm_routes),
+};
+
+static const struct regmap_config fs210x_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = FS210X_REG_MAX,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = fs210x_volatile_registers,
+};
+
+static int fs210x_detect_device(struct fs210x_priv *fs210x)
+{
+ u16 devid;
+ int ret;
+
+ ret = fs210x_reg_read(fs210x, FS210X_03H_DEVID, &devid);
+ if (ret)
+ return ret;
+
+ fs210x->devid = HI_U16(devid);
+
+ switch (fs210x->devid) {
+ case FS210X_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2104 detected\n");
+ break;
+ case FS2105S_DEVICE_ID:
+ dev_info(fs210x->dev, "FS2105S detected\n");
+ break;
+ default:
+ dev_err(fs210x->dev, "DEVID: 0x%04X dismatch\n", devid);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int fs210x_parse_dts(struct fs210x_priv *fs210x,
+ struct fs210x_platform_data *pdata)
+{
+ struct device_node *node = fs210x->dev->of_node;
+ int i, ret;
+
+ if (!node)
+ return 0;
+
+ ret = of_property_read_string(node, "firmware-name", &pdata->fwm_name);
+ if (ret)
+ pdata->fwm_name = FS210X_DEFAULT_FWM_NAME;
+
+ fs210x->gpio_sdz = devm_gpiod_get_optional(fs210x->dev,
+ "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(fs210x->gpio_sdz))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->gpio_sdz),
+ "Failed to get reset-gpios\n");
+
+ for (i = 0; i < FS210X_NUM_SUPPLIES; i++)
+ fs210x->supplies[i].supply = fs210x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(fs210x->dev,
+ ARRAY_SIZE(fs210x->supplies),
+ fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to get supplies\n");
+
+ return 0;
+}
+
+static void fs210x_deinit(struct fs210x_priv *fs210x)
+{
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 1); /* Active */
+ fsleep(10000); /* >= 10ms */
+
+ regulator_bulk_disable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+}
+
+static int fs210x_init(struct fs210x_priv *fs210x)
+{
+ int ret;
+
+ ret = fs210x_parse_dts(fs210x, &fs210x->pdata);
+ if (ret)
+ return ret;
+
+ fs210x->clk_bclk = devm_clk_get_optional(fs210x->dev, "bclk");
+ if (IS_ERR(fs210x->clk_bclk))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->clk_bclk),
+ "Failed to get bclk\n");
+
+ ret = regulator_bulk_enable(FS210X_NUM_SUPPLIES, fs210x->supplies);
+ if (ret)
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to enable supplies\n");
+
+ /* Make sure the SDZ pin is pulled down enough time. */
+ fsleep(10000); /* >= 10ms */
+ gpiod_set_value_cansleep(fs210x->gpio_sdz, 0); /* Deactivate */
+ fsleep(10000); /* >= 10ms */
+
+ ret = fs210x_detect_device(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return ret;
+ }
+
+ fs210x->scene_id = -1; /* Invalid scene */
+ fs210x->cur_scene = NULL;
+ fs210x->is_playing = false;
+ fs210x->is_inited = false;
+ fs210x->is_suspended = false;
+ fs210x->check_interval_ms = FS210X_FAULT_CHECK_INTERVAL_MS;
+
+ INIT_DELAYED_WORK(&fs210x->fault_check_work, fs210x_fault_check_work);
+ INIT_DELAYED_WORK(&fs210x->start_work, fs210x_start_work);
+ mutex_init(&fs210x->lock);
+
+ return 0;
+}
+
+static int fs210x_register_snd_component(struct fs210x_priv *fs210x)
+{
+ struct snd_soc_dai_driver *dai_drv;
+ static int instance_id;
+ int ret;
+
+ dai_drv = devm_kmemdup(fs210x->dev, &fs210x_dai,
+ sizeof(fs210x_dai), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ dai_drv->name = devm_kasprintf(fs210x->dev,
+ GFP_KERNEL, "%s-%d",
+ dai_drv->name, instance_id);
+ if (!dai_drv->name)
+ return -ENOMEM;
+
+ instance_id++;
+
+ if (fs210x->devid == FS2105S_DEVICE_ID) {
+ dai_drv->playback.rates = FS2105S_RATES;
+ dai_drv->capture.rates = FS2105S_RATES;
+ }
+
+ ret = snd_soc_register_component(fs210x->dev,
+ &fs210x_soc_component_dev,
+ dai_drv, 1);
+ return ret;
+}
+
+static ssize_t check_interval_ms_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", fs210x->check_interval_ms);
+}
+
+static ssize_t check_interval_ms_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct fs210x_priv *fs210x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = kstrtouint(buf, 10, &fs210x->check_interval_ms);
+ if (ret)
+ return -EINVAL;
+
+ return (ssize_t)count;
+}
+
+static DEVICE_ATTR_RW(check_interval_ms);
+
+static struct attribute *fs210x_attrs[] = {
+ &dev_attr_check_interval_ms.attr,
+ NULL,
+};
+
+static struct attribute_group fs210x_attr_group = {
+ .attrs = fs210x_attrs,
+};
+
+static int fs210x_i2c_probe(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x;
+ int ret;
+
+ fs210x = devm_kzalloc(&client->dev, sizeof(*fs210x), GFP_KERNEL);
+ if (!fs210x)
+ return -ENOMEM;
+
+ fs210x->i2c = client;
+ fs210x->dev = &client->dev;
+ i2c_set_clientdata(client, fs210x);
+
+ fs210x->regmap = devm_regmap_init_i2c(client, &fs210x_regmap);
+ if (IS_ERR(fs210x->regmap))
+ return dev_err_probe(fs210x->dev, PTR_ERR(fs210x->regmap),
+ "Failed to get regmap\n");
+
+ ret = fs210x_init(fs210x);
+ if (ret)
+ return ret;
+
+ ret = devm_device_add_group(fs210x->dev, &fs210x_attr_group);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to create sysfs group\n");
+ }
+
+ ret = fs210x_register_snd_component(fs210x);
+ if (ret) {
+ fs210x_deinit(fs210x);
+ return dev_err_probe(fs210x->dev, ret,
+ "Failed to register component\n");
+ }
+
+ return 0;
+}
+
+static void fs210x_i2c_remove(struct i2c_client *client)
+{
+ struct fs210x_priv *fs210x = i2c_get_clientdata(client);
+
+ snd_soc_unregister_component(fs210x->dev);
+ fs210x_deinit(fs210x);
+}
+
+static const struct i2c_device_id fs210x_i2c_id[] = {
+ { "fs2104" },
+ { "fs2105s" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
+
+static const struct of_device_id fs210x_of_match[] = {
+ { .compatible = "foursemi,fs2105s", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, fs210x_of_match);
+
+static struct i2c_driver fs210x_i2c_driver = {
+ .driver = {
+ .name = "fs210x",
+ .of_match_table = fs210x_of_match,
+ },
+ .id_table = fs210x_i2c_id,
+ .probe = fs210x_i2c_probe,
+ .remove = fs210x_i2c_remove,
+};
+
+module_i2c_driver(fs210x_i2c_driver);
+
+MODULE_AUTHOR("Nick Li <nick.li@foursemi.com>");
+MODULE_DESCRIPTION("FS2104/5S Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.h b/sound/soc/codecs/fs210x.h
new file mode 100644
index 000000000000..78e1760332ca
--- /dev/null
+++ b/sound/soc/codecs/fs210x.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * fs210x.h -- Driver for the FS2104/5S Audio Amplifier
+ *
+ * Copyright (C) 2016-2025 Shanghai FourSemi Semiconductor Co.,Ltd.
+ */
+
+#ifndef __FS210X_H__
+#define __FS210X_H__
+
+#define FS210X_00H_STATUS 0x00
+#define FS210X_03H_DEVID 0x03
+#define FS210X_05H_ANASTAT 0x05
+#define FS210X_06H_DIGSTAT 0x06
+#define FS210X_0BH_ACCKEY 0x0B
+#define FS210X_0FH_I2CADDR 0x0F
+#define FS210X_10H_PWRCTRL 0x10
+#define FS210X_11H_SYSCTRL 0x11
+#define FS210X_17H_I2SCTRL 0x17
+#define FS210X_30H_DACCTRL 0x30
+#define FS210X_39H_LVOLCTRL 0x39
+#define FS210X_3AH_RVOLCTRL 0x3A
+#define FS210X_42H_DACEQWL 0x42
+#define FS210X_46H_DACEQA 0x46
+#define FS210X_A1H_PLLCTRL1 0xA1
+#define FS210X_A2H_PLLCTRL2 0xA2
+#define FS210X_A3H_PLLCTRL3 0xA3
+#define FS210X_ABH_INTSTAT 0xAB
+#define FS210X_ACH_INTSTATR 0xAC
+
+#define FS210X_05H_PVDD_SHIFT 14
+#define FS210X_05H_PVDD_MASK BIT(14)
+#define FS210X_05H_OCDL_SHIFT 13
+#define FS210X_05H_OCDL_MASK BIT(13)
+#define FS210X_05H_UVDL_SHIFT 12
+#define FS210X_05H_UVDL_MASK BIT(12)
+#define FS210X_05H_OVDL_SHIFT 11
+#define FS210X_05H_OVDL_MASK BIT(11)
+#define FS210X_05H_OTPDL_SHIFT 10
+#define FS210X_05H_OTPDL_MASK BIT(10)
+#define FS210X_05H_OCRDL_SHIFT 9
+#define FS210X_05H_OCRDL_MASK BIT(9)
+#define FS210X_05H_OCLDL_SHIFT 8
+#define FS210X_05H_OCLDL_MASK BIT(8)
+#define FS210X_05H_DCRDL_SHIFT 7
+#define FS210X_05H_DCRDL_MASK BIT(7)
+#define FS210X_05H_DCLDL_SHIFT 6
+#define FS210X_05H_DCLDL_MASK BIT(6)
+#define FS210X_05H_SRDL_SHIFT 5
+#define FS210X_05H_SRDL_MASK BIT(5)
+#define FS210X_05H_OTWDL_SHIFT 4
+#define FS210X_05H_OTWDL_MASK BIT(4)
+#define FS210X_05H_AMPS_SHIFT 3
+#define FS210X_05H_AMPS_MASK BIT(3)
+#define FS210X_05H_PLLS_SHIFT 1
+#define FS210X_05H_PLLS_MASK BIT(1)
+#define FS210X_05H_ANAS_SHIFT 0
+#define FS210X_05H_ANAS_MASK BIT(0)
+#define FS210X_17H_I2SSR_SHIFT 12
+#define FS210X_17H_I2SSR_MASK GENMASK(15, 12)
+#define FS210X_30H_RMUTE_SHIFT 8
+#define FS210X_30H_LMUTE_SHIFT 4
+
+#define FS210X_0BH_ACCKEY_ON 0x0091
+#define FS210X_0BH_ACCKEY_OFF 0x0000
+#define FS210X_10H_I2C_RESET 0x0002
+#define FS210X_11H_DPS_HIZ 0x0100
+#define FS210X_11H_DPS_PWDN 0x0000
+#define FS210X_11H_DPS_PLAY 0x0300
+#define FS210X_46H_CAM_BURST_L 0x8000
+#define FS210X_46H_CAM_BURST_R 0x8200
+#define FS2105S_46H_CAM_BURST_W 0x8400
+#define FS210X_46H_CAM_CLEAR 0x0000
+
+#endif /* __FS210X_H__ */
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
new file mode 100644
index 000000000000..1f165e46701f
--- /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 <marek@goldelico.com>
+ *
+ * Based on wm8727.c driver
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+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 *)&gtm601_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 <marek@goldelico.com>");
+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 000000000000..b9caae7e4817
--- /dev/null
+++ b/sound/soc/codecs/hda-dai.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <sound/soc.h>
+#include <sound/hda_codec.h>
+#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;
+ unsigned int bits;
+ int ret;
+
+ codec = dev_to_hda_codec(dai->dev);
+ stream = substream->runtime->private_data;
+ stream_info = snd_soc_dai_get_dma_data(dai, substream);
+
+ bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ stream_info->maxbps);
+ format = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+
+ 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 000000000000..ede980cc6050
--- /dev/null
+++ b/sound/soc/codecs/hda.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright(c) 2021-2022 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#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->subformats = pcm->stream[dir].subformats;
+ 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->subformats = pcm->stream[dir].subformats;
+ 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);
+ return ret;
+ }
+
+ /* 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);
+
+ /* Complement pm_runtime_get_sync(bus) in probe */
+ 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 hda_codec_driver *driver = hda_codec_to_driver(codec);
+ struct hdac_device *hdev = &codec->core;
+ struct hdac_bus *bus = hdev->bus;
+ struct hdac_ext_link *hlink;
+ 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, "codec create 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, "set name: %s failed: %d\n", codec->preset->name, ret);
+ goto err;
+ }
+
+ ret = snd_hdac_regmap_init(&codec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "regmap init failed: %d\n", ret);
+ goto err;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = driver->ops->probe(codec, codec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "codec init 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 (driver->ops->remove)
+ driver->ops->remove(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_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 hda_codec_driver *driver = hda_codec_to_driver(codec);
+ 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 (driver->ops->remove)
+ driver->ops->remove(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_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;
+
+ if (hda_codec_is_display(codec) && !hdev->bus->audio_component) {
+ dev_dbg(&hdev->dev, "no i915, skip registration for 0x%08x\n", hdev->vendor_id);
+ return -ENODEV;
+ }
+
+ 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 <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/hda.h b/sound/soc/codecs/hda.h
new file mode 100644
index 000000000000..59308cc6afef
--- /dev/null
+++ b/sound/soc/codecs/hda.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright(c) 2021-2022 Intel Corporation
+ *
+ * Author: Cezary Rojewski <cezary.rojewski@intel.com>
+ */
+
+#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 000000000000..afd8edf10fdc
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.c
@@ -0,0 +1,679 @@
+// 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 <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/hda_codec.h>
+#include <sound/hda_register.h>
+
+#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)
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+static char *loadable_patch[HDA_MAX_CODECS];
+
+module_param_array_named(patch, loadable_patch, charp, NULL, 0444);
+MODULE_PARM_DESC(patch, "Patch file array for Intel HD audio interface. The array index is the codec address.");
+#endif
+
+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;
+ unsigned int bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ maxbps = dai->driver->playback.sig_bits;
+ else
+ maxbps = dai->driver->capture.sig_bits;
+ bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD, maxbps);
+
+ hda_pvt = snd_soc_component_get_drvdata(component);
+ format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
+ if (!format_val) {
+ dev_err(dai->dev,
+ "%s: invalid format_val, rate=%d, ch=%d, format=%d, maxbps=%d\n",
+ __func__,
+ 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;
+ unsigned int format_val;
+ struct hda_pcm *pcm;
+ unsigned int stream;
+ int ret = 0;
+
+ 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];
+
+ 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(dai->dev, "%s: failed %d\n", __func__, 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;
+ int ret;
+
+ 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];
+
+ ret = hda_stream->ops.open(hda_stream, hda_pvt->codec, substream);
+ if (ret < 0)
+ dev_err(dai->dev, "%s: failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+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(dai->dev, "%s: invalid dai id %d\n", __func__, 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(dai->dev, "%s: didn't find PCM for DAI %s\n", __func__, 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 hda_codec_driver *driver = hda_codec_to_driver(hcodec);
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
+ if (!hlink) {
+ dev_err(&hdev->dev, "%s: hdac link not found\n", __func__);
+ 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, "%s: failed to create hda codec %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+
+#ifdef CONFIG_SND_HDA_PATCH_LOADER
+ if (loadable_patch[hda_pvt->dev_index] && *loadable_patch[hda_pvt->dev_index]) {
+ const struct firmware *fw;
+
+ dev_info(&hdev->dev, "Applying patch firmware '%s'\n",
+ loadable_patch[hda_pvt->dev_index]);
+ ret = request_firmware(&fw, loadable_patch[hda_pvt->dev_index],
+ &hdev->dev);
+ if (ret < 0)
+ goto error_no_pm;
+ if (fw) {
+ ret = snd_hda_load_patch(hcodec->bus, fw->size, fw->data);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: failed to load hda patch %d\n", __func__, ret);
+ goto error_no_pm;
+ }
+ release_firmware(fw);
+ }
+ }
+#endif
+ /*
+ * 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, "%s: name failed %s\n", __func__, hcodec->preset->name);
+ goto error_pm;
+ }
+
+ ret = snd_hdac_regmap_init(&hcodec->core);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: regmap init failed\n", __func__);
+ goto error_pm;
+ }
+
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto error_regmap;
+ }
+
+ ret = driver->ops->probe(hcodec, hcodec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
+ goto error_regmap;
+ }
+
+ ret = snd_hda_codec_parse_pcms(hcodec);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: unable to map pcms to dai %d\n", __func__, 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, "%s: unable to create controls %d\n",
+ __func__, 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 (driver->ops->remove)
+ driver->ops->remove(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 hda_codec_driver *driver = hda_codec_to_driver(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, "%s: hdac link not found\n", __func__);
+ return;
+ }
+
+ pm_runtime_disable(&hdev->dev);
+ snd_hdac_ext_bus_link_put(hdev->bus, hlink);
+
+ if (driver->ops->remove)
+ driver->ops->remove(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, "%s: hdac link not found\n", __func__);
+ 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, "%s: failed to register HDA codec %d\n", __func__, 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<rakesh.a.ughreja@intel.com>");
diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h
new file mode 100644
index 000000000000..d03a5d4e7288
--- /dev/null
+++ b/sound/soc/codecs/hdac_hda.h
@@ -0,0 +1,34 @@
+/* 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;
+ int dev_index;
+};
+
+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 000000000000..e50afd0bfcec
--- /dev/null
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -0,0 +1,2043 @@
+// 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 <samreen.nilofer@intel.com>
+ * Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/hdmi.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_eld.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/hdaudio_ext.h>
+#include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hda_chmap.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 = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_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;
+ unsigned int bits;
+ int format;
+
+ dai_map = &hdmi->dai_map[dai->id];
+
+ bits = snd_hdac_stream_format_bits(params_format(hparams), SNDRV_PCM_SUBFORMAT_STD,
+ dai->driver->playback.sig_bits);
+ format = snd_hdac_stream_format(params_channels(hparams), bits, params_rate(hparams));
+
+ 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 (!(snd_hdac_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, snd_hdac_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 = snd_hdac_get_wcaps(hdev, cvt->nid);
+ chans = snd_hdac_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,
+ NULL,
+ &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 (snd_hdac_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 (snd_hdac_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_array(&hdev->dev, items, num_items, sizeof(items[0]), 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_ratelimited(&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_ratelimited(&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_dbg(&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 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, NULL, &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 = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_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 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);
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops hdac_hdmi_pm = {
+ RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+ 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 = pm_ptr(&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<samreen.nilofer@intel.com>");
+MODULE_AUTHOR("Subhransu S. Prusty<subhransu.s.prusty@intel.com>");
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
new file mode 100644
index 000000000000..e1933f733af1
--- /dev/null
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -0,0 +1,1212 @@
+// 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 <jsarha@ti.com>
+ */
+#include <linux/module.h>
+#include <linux/string.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_drm_eld.h>
+#include <sound/hdmi-codec.h>
+#include <sound/pcm_iec958.h>
+
+#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+#include <drm/drm_eld.h>
+
+#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 where ca_id must exist in hdmi_codec_8ch_chmaps
+ * 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 },
+ { .ca_id = 0x03, .n_ch = 4,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x02, .n_ch = 4,
+ .mask = FL | FR | FC },
+ { .ca_id = 0x01, .n_ch = 4,
+ .mask = FL | FR | LFE },
+ { .ca_id = 0x0b, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RL | RR },
+ { .ca_id = 0x0a, .n_ch = 6,
+ .mask = FL | FR | FC | RL | RR },
+ { .ca_id = 0x09, .n_ch = 6,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 6,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 6,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 6,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 6,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 6,
+ .mask = FL | FR | RC },
+ { .ca_id = 0x13, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RLC | RRC },
+ { .ca_id = 0x1f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | FLC | FRC },
+ { .ca_id = 0x12, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RLC | RRC },
+ { .ca_id = 0x1e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | FLC | FRC },
+ { .ca_id = 0x11, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RLC | RRC },
+ { .ca_id = 0x1d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | FLC | FRC },
+ { .ca_id = 0x10, .n_ch = 8,
+ .mask = FL | FR | RL | RR | RLC | RRC },
+ { .ca_id = 0x1c, .n_ch = 8,
+ .mask = FL | FR | RL | RR | FLC | FRC },
+ { .ca_id = 0x0f, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR | RC },
+ { .ca_id = 0x1b, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FC | FLC | FRC },
+ { .ca_id = 0x0e, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR | RC },
+ { .ca_id = 0x1a, .n_ch = 8,
+ .mask = FL | FR | RC | FC | FLC | FRC },
+ { .ca_id = 0x0d, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR | RC },
+ { .ca_id = 0x19, .n_ch = 8,
+ .mask = FL | FR | LFE | RC | FLC | FRC },
+ { .ca_id = 0x0c, .n_ch = 8,
+ .mask = FL | FR | RC | RL | RR },
+ { .ca_id = 0x18, .n_ch = 8,
+ .mask = FL | FR | RC | FLC | FRC },
+ { .ca_id = 0x17, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | FLC | FRC },
+ { .ca_id = 0x16, .n_ch = 8,
+ .mask = FL | FR | FC | FLC | FRC },
+ { .ca_id = 0x15, .n_ch = 8,
+ .mask = FL | FR | LFE | FLC | FRC },
+ { .ca_id = 0x14, .n_ch = 8,
+ .mask = FL | FR | FLC | FRC },
+ { .ca_id = 0x0b, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RL | RR },
+ { .ca_id = 0x0a, .n_ch = 8,
+ .mask = FL | FR | FC | RL | RR },
+ { .ca_id = 0x09, .n_ch = 8,
+ .mask = FL | FR | LFE | RL | RR },
+ { .ca_id = 0x08, .n_ch = 8,
+ .mask = FL | FR | RL | RR },
+ { .ca_id = 0x07, .n_ch = 8,
+ .mask = FL | FR | LFE | FC | RC },
+ { .ca_id = 0x06, .n_ch = 8,
+ .mask = FL | FR | FC | RC },
+ { .ca_id = 0x05, .n_ch = 8,
+ .mask = FL | FR | LFE | RC },
+ { .ca_id = 0x04, .n_ch = 8,
+ .mask = FL | FR | RC },
+ { .ca_id = 0x03, .n_ch = 8,
+ .mask = FL | FR | LFE | FC },
+ { .ca_id = 0x02, .n_ch = 8,
+ .mask = FL | FR | FC },
+ { .ca_id = 0x01, .n_ch = 8,
+ .mask = FL | FR | LFE },
+};
+
+struct hdmi_codec_priv {
+ struct hdmi_codec_pdata hcd;
+ uint8_t eld[MAX_ELD_BYTES];
+ struct snd_parsed_hdmi_eld eld_parsed;
+ 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];
+ struct snd_info_entry *proc_entry;
+};
+
+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;
+
+ if (hcp->chmap_idx != HDMI_CODEC_CHMAP_IDX_UNKNOWN)
+ 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;
+
+ snd_parse_eld(dai->dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+
+ 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.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 const 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;
+}
+
+#ifdef CONFIG_SND_PROC_FS
+static void print_eld_info(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct hdmi_codec_priv *hcp = entry->private_data;
+
+ snd_print_eld_info(&hcp->eld_parsed, buffer);
+}
+
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_card *card = component->card;
+ struct snd_soc_dai *d;
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_info_entry *entry;
+ char name[32];
+ int err, i, id = 0;
+
+ /*
+ * To avoid duplicate proc entry, find its rtd and use rtd->id
+ * instead of dai->id
+ */
+ for_each_card_rtds(card, rtd) {
+ for_each_rtd_dais(rtd, i, d)
+ if (d == dai) {
+ id = rtd->id;
+ goto found;
+ }
+ }
+found:
+ snprintf(name, sizeof(name), "eld#%d", id);
+ err = snd_card_proc_new(card->snd_card, name, &entry);
+ if (err < 0)
+ return err;
+
+ snd_info_set_text_ops(entry, hcp, print_eld_info);
+ hcp->proc_entry = entry;
+
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+ snd_info_free_entry(hcp->proc_entry);
+ hcp->proc_entry = NULL;
+}
+#else
+static int hdmi_dai_proc_new(struct hdmi_codec_priv *hcp,
+ struct snd_soc_dai *dai)
+{
+ return 0;
+}
+
+static void hdmi_dai_proc_free(struct hdmi_codec_priv *hcp)
+{
+}
+#endif
+
+static int hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+ 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 hdmi_dai_proc_new(hcp, dai);
+}
+
+static int hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+ struct hdmi_codec_priv *hcp =
+ snd_soc_component_get_drvdata(dai->component);
+
+ hdmi_dai_proc_free(hcp);
+ 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_AVOUT);
+ hcp->jack_status = jack_status;
+ }
+}
+
+static void plugged_cb(struct device *dev, bool plugged)
+{
+ struct hdmi_codec_priv *hcp = dev_get_drvdata(dev);
+ int ret;
+
+ if (plugged) {
+ if (hcp->hcd.ops->get_eld) {
+ hcp->hcd.ops->get_eld(dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
+ ret = snd_parse_eld(dev, &hcp->eld_parsed,
+ hcp->eld, sizeof(hcp->eld));
+ if (ret < 0)
+ dev_dbg(dev, "Failed to parse ELD: %d\n", ret);
+ else
+ snd_show_eld(dev, &hcp->eld_parsed);
+ }
+ hdmi_codec_jack_report(hcp, SND_JACK_AVOUT);
+ } 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_AVOUT);
+
+ 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,
+ .remove = hdmi_dai_remove,
+ .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,
+ .prepare = hdmi_codec_prepare,
+ .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, hcp->hcd.data);
+
+ 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->i2s_formats) {
+ daidrv[i].playback.formats = hcd->i2s_formats;
+ daidrv[i].capture.formats = hcd->i2s_formats;
+ }
+ 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 <jsarha@ti.com>");
+MODULE_DESCRIPTION("HDMI Audio Codec Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
deleted file mode 100644
index bd42ad34e004..000000000000
--- a/sound/soc/codecs/hdmi.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-#include <linux/module.h>
-#include <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-codec"
-
-static const struct snd_soc_dapm_widget hdmi_widgets[] = {
- SND_SOC_DAPM_INPUT("RX"),
- SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
- { "Capture", NULL, "RX" },
- { "TX", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai = {
- .name = "hdmi-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 8,
- .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,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
- .sig_bits = 24,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 2,
- .channels_max = 2,
- .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,
- .formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE,
- },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
- { .compatible = "linux,hdmi-audio", },
- { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-static struct snd_soc_codec_driver hdmi_codec = {
- .dapm_widgets = hdmi_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
- .ignore_pmdown_time = true,
-};
-
-static int hdmi_codec_probe(struct platform_device *pdev)
-{
- return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
- &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
- .driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
- },
-
- .probe = hdmi_codec_probe,
- .remove = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/ics43432.c b/sound/soc/codecs/ics43432.c
new file mode 100644
index 000000000000..58a382254718
--- /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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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 <ricardw@axis.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
new file mode 100644
index 000000000000..a03d4e5e7d14
--- /dev/null
+++ b/sound/soc/codecs/idt821034.c
@@ -0,0 +1,1183 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// IDT821034 ALSA SoC driver
+//
+// Copyright 2022 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/bitrev.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 const 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 int 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);
+
+ mutex_unlock(&idt821034->mutex);
+
+ if (ret)
+ dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
+ offset, ch, mask, ret);
+
+ return ret;
+}
+
+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;
+
+ ret = idt821034_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ 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 out 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 <herve.codina@bootlin.com>");
+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 000000000000..fdd19f8e8864
--- /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<zhengsq@rock-chips.com>
+ */
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/io.h>
+
+#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 = 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 000000000000..44bb2404198d
--- /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<zhengsq@rock-chips.com>
+ */
+
+#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
index 3a89ce66d51d..b7a94631d77d 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* isabelle.c - Low power high fidelity audio codec driver
*
* Copyright (c) 2012 Texas Instruments, Inc
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- *
* Initially based on sound/soc/codecs/twl6040.c
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -33,7 +28,7 @@
/* Register default values for ISABELLE driver. */
-static struct reg_default isabelle_reg_defs[] = {
+static const struct reg_default isabelle_reg_defs[] = {
{ 0, 0x00 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -865,31 +860,31 @@ static const struct snd_soc_dapm_route isabelle_intercon[] = {
{ "LINEOUT2", NULL, "LINEOUT2 Driver" },
};
-static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute)
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG,
+ 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)
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG,
+ 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)
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG,
+ 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_codec *codec,
+static int isabelle_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
switch (level) {
@@ -899,18 +894,16 @@ static int isabelle_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, BIT(0));
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+ snd_soc_component_update_bits(component, ISABELLE_PWR_EN_REG,
ISABELLE_CHIP_EN, 0);
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -918,7 +911,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 aif = 0;
unsigned int fs_val = 0;
@@ -954,7 +947,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_FS_RATE_CFG_REG,
ISABELLE_FS_RATE_MASK, fs_val);
/* bit size */
@@ -969,7 +962,7 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
ISABELLE_AIF_LENGTH_MASK, aif);
return 0;
@@ -977,14 +970,14 @@ static int isabelle_hw_params(struct snd_pcm_substream *substream,
static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
unsigned int aif_val = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val &= ~ISABELLE_AIF_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val |= ISABELLE_AIF_MS;
break;
default:
@@ -1005,7 +998,7 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+ snd_soc_component_update_bits(component, ISABELLE_INTF_CFG_REG,
(ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
return 0;
@@ -1018,25 +1011,28 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S32_LE)
-static struct snd_soc_dai_ops isabelle_hs_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_hs_mute,
+ .mute_stream = isabelle_hs_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops isabelle_hf_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_hf_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_hf_mute,
+ .mute_stream = isabelle_hf_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops isabelle_line_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_line_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
- .digital_mute = isabelle_line_mute,
+ .mute_stream = isabelle_line_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops isabelle_ul_dai_ops = {
+static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
};
@@ -1089,15 +1085,16 @@ static struct snd_soc_dai_driver isabelle_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_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),
- .idle_bias_off = true,
+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 = {
@@ -1110,8 +1107,7 @@ static const struct regmap_config isabelle_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int isabelle_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int isabelle_i2c_probe(struct i2c_client *i2c)
{
struct regmap *isabelle_regmap;
int ret = 0;
@@ -1125,25 +1121,19 @@ static int isabelle_i2c_probe(struct i2c_client *i2c,
}
i2c_set_clientdata(i2c, isabelle_regmap);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_isabelle, isabelle_dai,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
return ret;
}
-static int isabelle_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id isabelle_i2c_id[] = {
- { "isabelle", 0 },
+ { "isabelle" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
@@ -1151,10 +1141,8 @@ MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
static struct i2c_driver isabelle_i2c_driver = {
.driver = {
.name = "isabelle",
- .owner = THIS_MODULE,
},
.probe = isabelle_i2c_probe,
- .remove = isabelle_i2c_remove,
.id_table = isabelle_i2c_id,
};
diff --git a/sound/soc/codecs/isabelle.h b/sound/soc/codecs/isabelle.h
index 96d839a8c956..23afc77cdc99 100644
--- a/sound/soc/codecs/isabelle.h
+++ b/sound/soc/codecs/isabelle.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* isabelle.h - Low power high fidelity audio codec driver header file
*
* Copyright (c) 2012 Texas Instruments, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
*/
#ifndef _ISABELLE_H
diff --git a/sound/soc/codecs/jz4725b.c b/sound/soc/codecs/jz4725b.c
new file mode 100644
index 000000000000..39cebaa167be
--- /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 <paul@crapouillou.net>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 933f4476d76c..d1cea93bdb59 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -1,15 +1,8 @@
-/*
- * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// JZ4740 CODEC driver
+//
+// Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -78,11 +71,10 @@ struct jz4740_codec {
struct regmap *regmap;
};
-static const unsigned int jz4740_mic_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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);
@@ -158,7 +150,7 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
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_codec_get_drvdata(dai->codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(dai->component);
uint32_t val;
switch (params_rate(params)) {
@@ -222,28 +214,26 @@ static struct snd_soc_dai_driver jz4740_codec_dai = {
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8,
},
.ops = &jz4740_codec_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static void jz4740_codec_wakeup(struct regmap *regmap)
{
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, JZ4740_CODEC_1_RESET);
udelay(2);
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
- JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
+ 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_codec *codec,
+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_codec_get_drvdata(codec);
+ struct jz4740_codec *jz4740_codec = snd_soc_component_get_drvdata(component);
struct regmap *regmap = jz4740_codec->regmap;
unsigned int mask;
- unsigned int value;
switch (level) {
case SND_SOC_BIAS_ON:
@@ -252,43 +242,35 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- value = 0;
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ 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;
- value = JZ4740_CODEC_1_VREF_DISABLE |
- JZ4740_CODEC_1_VREF_AMP_DISABLE |
- JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
break;
case SND_SOC_BIAS_OFF:
mask = JZ4740_CODEC_1_SUSPEND;
- value = JZ4740_CODEC_1_SUSPEND;
-
- regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regmap_set_bits(regmap, JZ4740_REG_CODEC_1, mask);
regcache_mark_dirty(regmap);
break;
default:
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
-static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
+static int jz4740_codec_dev_probe(struct snd_soc_component *component)
{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -296,17 +278,19 @@ static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
- .probe = jz4740_codec_dev_probe,
- .set_bias_level = jz4740_codec_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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 = {
@@ -317,14 +301,13 @@ static const struct regmap_config jz4740_codec_regmap_config = {
.reg_defaults = jz4740_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
- struct resource *mem;
void __iomem *base;
jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
@@ -332,8 +315,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
if (!jz4740_codec)
return -ENOMEM;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem);
+ base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
return PTR_ERR(base);
@@ -344,7 +326,7 @@ static int jz4740_codec_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, jz4740_codec);
- ret = snd_soc_register_codec(&pdev->dev,
+ 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");
@@ -352,18 +334,17 @@ static int jz4740_codec_probe(struct platform_device *pdev)
return ret;
}
-static int jz4740_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
+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,
- .remove = jz4740_codec_remove,
.driver = {
.name = "jz4740-codec",
+ .of_match_table = jz4740_codec_of_matches,
},
};
diff --git a/sound/soc/codecs/jz4760.c b/sound/soc/codecs/jz4760.c
new file mode 100644
index 000000000000..e04af1b9ace8
--- /dev/null
+++ b/sound/soc/codecs/jz4760.c
@@ -0,0 +1,871 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4760 CODEC driver
+//
+// Copyright (C) 2021, Christophe Branchereau <cbranchereau@gmail.com>
+// Copyright (C) 2021, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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, &reg);
+
+ 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[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR6, JZ4760_CODEC_REG_GCR5,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4760_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4760_CODEC_REG_GCR2, JZ4760_CODEC_REG_GCR1,
+ REG_GCR_GAIN_OFFSET, REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+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 const 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 <cbranchereau@gmail.com>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/jz4770.c b/sound/soc/codecs/jz4770.c
new file mode 100644
index 000000000000..312202ab5cea
--- /dev/null
+++ b/sound/soc/codecs/jz4770.c
@@ -0,0 +1,919 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Ingenic JZ4770 CODEC driver
+//
+// Copyright (C) 2012, Maarten ter Huurne <maarten@treewalker.org>
+// Copyright (C) 2019, Paul Cercueil <paul@crapouillou.net>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/time64.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_DACR,
+ JZ4770_CODEC_REG_GCR_DACL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, dac_tlv),
+};
+
+static const struct snd_kcontrol_new jz4770_codec_hp_playback_controls[] = {
+ SOC_DAPM_DOUBLE_R_TLV("Volume", JZ4770_CODEC_REG_GCR_HPR,
+ JZ4770_CODEC_REG_GCR_HPL, REG_GCR_GAIN_OFFSET,
+ REG_GCR_GAIN_MAX, 1, out_tlv),
+};
+
+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 const 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 <maarten@treewalker.org>");
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
deleted file mode 100644
index 5353af58862c..000000000000
--- a/sound/soc/codecs/l3.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * L3 code
- *
- * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
- *
- * 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.
- *
- *
- * based on:
- *
- * L3 bus algorithm module.
- *
- * Copyright (C) 2001 Russell King, All Rights Reserved.
- *
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-
-#include <sound/l3.h>
-
-/*
- * Send one byte of data to the chip. Data is latched into the chip on
- * the rising edge of the clock.
- */
-static void sendbyte(struct l3_pins *adap, unsigned int byte)
-{
- int i;
-
- for (i = 0; i < 8; i++) {
- adap->setclk(0);
- udelay(adap->data_hold);
- adap->setdat(byte & 1);
- udelay(adap->data_setup);
- adap->setclk(1);
- udelay(adap->clock_high);
- byte >>= 1;
- }
-}
-
-/*
- * Send a set of bytes to the chip. We need to pulse the MODE line
- * between each byte, but never at the start nor at the end of the
- * transfer.
- */
-static void sendbytes(struct l3_pins *adap, const u8 *buf,
- int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (i) {
- udelay(adap->mode_hold);
- adap->setmode(0);
- udelay(adap->mode);
- }
- adap->setmode(1);
- udelay(adap->mode_setup);
- sendbyte(adap, buf[i]);
- }
-}
-
-int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
-{
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(1);
- udelay(adap->mode);
-
- adap->setmode(0);
- udelay(adap->mode_setup);
- sendbyte(adap, addr);
- udelay(adap->mode_hold);
-
- sendbytes(adap, data, len);
-
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(0);
-
- return len;
-}
-EXPORT_SYMBOL_GPL(l3_write);
-
-MODULE_DESCRIPTION("L3 bit-banging driver");
-MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index a924bb9d7886..26cdb750cbca 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* LM4857 AMP driver
*
@@ -5,12 +6,6 @@
* Author: Graeme Gregory
* graeme.gregory@wolfsonmicro.com
* Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/init.h>
@@ -23,11 +18,6 @@
#include <sound/soc.h>
#include <sound/tlv.h>
-struct lm4857 {
- struct regmap *regmap;
- uint8_t mode;
-};
-
static const struct reg_default lm4857_default_regs[] = {
{ 0x0, 0x00 },
{ 0x1, 0x00 },
@@ -46,66 +36,33 @@ static const struct reg_default lm4857_default_regs[] = {
#define LM4857_WAKEUP 5
#define LM4857_EPGAIN 4
-static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = lm4857->mode;
-
- return 0;
-}
-
-static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
- uint8_t value = ucontrol->value.integer.value[0];
-
- lm4857->mode = value;
-
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
-
- return 1;
-}
-
-static int lm4857_set_bias_level(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
- lm4857->mode + 6);
- break;
- case SND_SOC_BIAS_STANDBY:
- regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
- break;
- default:
- break;
- }
-
- codec->dapm.bias_level = level;
-
- return 0;
-}
+static const unsigned int lm4857_mode_values[] = {
+ 0,
+ 6,
+ 7,
+ 8,
+ 9,
+};
-static const char *lm4857_mode[] = {
+static const char * const lm4857_mode_texts[] = {
+ "Off",
"Earpiece",
"Loudspeaker",
"Loudspeaker + Headphone",
"Headphone",
};
-static SOC_ENUM_SINGLE_EXT_DECL(lm4857_mode_enum, lm4857_mode);
+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"),
@@ -127,24 +84,18 @@ static const struct snd_kcontrol_new lm4857_controls[] = {
LM4857_WAKEUP, 1, 0),
SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
LM4857_EPGAIN, 1, 0),
-
- SOC_ENUM_EXT("Mode", lm4857_mode_enum,
- lm4857_get_mode, lm4857_set_mode),
};
-/* There is a demux between the input signal and the output signals.
- * Currently there is no easy way to model it in ASoC and since it does not make
- * much of a difference in practice simply connect the input direclty to the
- * outputs. */
static const struct snd_soc_dapm_route lm4857_routes[] = {
- {"LS", NULL, "IN"},
- {"HP", NULL, "IN"},
- {"EP", NULL, "IN"},
+ { "Mode", NULL, "IN" },
+ { "LS", "Loudspeaker", "Mode" },
+ { "LS", "Loudspeaker + Headphone", "Mode" },
+ { "HP", "Headphone", "Mode" },
+ { "HP", "Loudspeaker + Headphone", "Mode" },
+ { "EP", "Earpiece", "Mode" },
};
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
- .set_bias_level = lm4857_set_bias_level,
-
+static const struct snd_soc_component_driver lm4857_component_driver = {
.controls = lm4857_controls,
.num_controls = ARRAY_SIZE(lm4857_controls),
.dapm_widgets = lm4857_dapm_widgets,
@@ -164,32 +115,20 @@ static const struct regmap_config lm4857_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
};
-static int lm4857_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm4857_i2c_probe(struct i2c_client *i2c)
{
- struct lm4857 *lm4857;
-
- lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
- if (!lm4857)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, lm4857);
-
- lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
- if (IS_ERR(lm4857->regmap))
- return PTR_ERR(lm4857->regmap);
+ struct regmap *regmap;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
-}
+ regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
-static int lm4857_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c->dev,
+ &lm4857_component_driver, NULL, 0);
}
static const struct i2c_device_id lm4857_i2c_id[] = {
- { "lm4857", 0 },
+ { "lm4857" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
@@ -197,10 +136,8 @@ MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
static struct i2c_driver lm4857_i2c_driver = {
.driver = {
.name = "lm4857",
- .owner = THIS_MODULE,
},
.probe = lm4857_i2c_probe,
- .remove = lm4857_i2c_remove,
.id_table = lm4857_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index c4dfde9bdf1c..ab89af7965bf 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lm49453.c - LM49453 ALSA Soc Audio driver
*
* Copyright (c) 2012 Texas Instruments, Inc
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
* Initially based on sound/soc/codecs/wm8350.c
*/
@@ -30,7 +27,7 @@
#include <asm/div64.h>
#include "lm49453.h"
-static struct reg_default lm49453_reg_defs[] = {
+static const struct reg_default lm49453_reg_defs[] = {
{ 0, 0x00 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -188,7 +185,6 @@ static struct reg_default lm49453_reg_defs[] = {
/* codec private data */
struct lm49453_priv {
struct regmap *regmap;
- int fs_rate;
};
/* capture path controls */
@@ -1111,14 +1107,11 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
u16 clk_div = 0;
- lm49453->fs_rate = params_rate(params);
-
/* Setting DAC clock dividers based on substream sample rate. */
- switch (lm49453->fs_rate) {
+ switch (params_rate(params)) {
case 8000:
case 16000:
case 32000:
@@ -1138,32 +1131,32 @@ static int lm49453_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_write(codec, LM49453_P0_ADC_CLK_DIV_REG, clk_div);
- snd_soc_write(codec, LM49453_P0_DAC_HP_CLK_DIV_REG, clk_div);
+ 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_codec *codec = codec_dai->codec;
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
aif_val = 0;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
+ case SND_SOC_DAIFMT_CBC_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
+ case SND_SOC_DAIFMT_CBP_CFC:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
aif_val = LM49453_AUDIO_PORT1_BASIC_CLK_MS |
LM49453_AUDIO_PORT1_BASIC_SYNC_MS;
break;
@@ -1189,11 +1182,11 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
+ 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_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
+ snd_soc_component_write(component, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
return 0;
}
@@ -1201,7 +1194,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
u16 pll_clk = 0;
switch (freq) {
@@ -1213,57 +1206,55 @@ static int lm49453_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
break;
case 48000:
case 32576:
- /* fll clk slection */
- pll_clk = BIT(4);
return 0;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG, BIT(4), pll_clk);
+ 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)
+static int lm49453_hp_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(1)|BIT(0),
+ 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)
+static int lm49453_lo_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(3)|BIT(2),
+ 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)
+static int lm49453_ls_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(5)|BIT(4),
+ 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)
+static int lm49453_ep_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(4),
+ 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)
+static int lm49453_ha_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- snd_soc_update_bits(dai->codec, LM49453_P0_DAC_DSP_REG, BIT(7)|BIT(6),
+ 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_codec *codec,
+static int lm49453_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct lm49453_priv *lm49453 = snd_soc_codec_get_drvdata(codec);
+ struct lm49453_priv *lm49453 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -1271,21 +1262,19 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(lm49453->regmap);
- snd_soc_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ 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_update_bits(codec, LM49453_P0_PMC_SETUP_REG,
+ snd_soc_component_update_bits(component, LM49453_P0_PMC_SETUP_REG,
LM49453_PMC_SETUP_CHIP_EN, 0);
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -1293,39 +1282,44 @@ static int lm49453_set_bias_level(struct snd_soc_codec *codec,
#define LM49453_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_ops lm49453_headset_dai_ops = {
+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,
- .digital_mute = lm49453_hp_mute,
+ .mute_stream = lm49453_hp_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
+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,
- .digital_mute = lm49453_ls_mute,
+ .mute_stream = lm49453_ls_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
+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,
- .digital_mute = lm49453_ha_mute,
+ .mute_stream = lm49453_ha_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops lm49453_ep_dai_ops = {
+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,
- .digital_mute = lm49453_ep_mute,
+ .mute_stream = lm49453_ep_mute,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
+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,
- .digital_mute = lm49453_lo_mute,
+ .mute_stream = lm49453_lo_mute,
+ .no_capture_mute = 1,
};
/* LM49453 dai structure. */
@@ -1347,7 +1341,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
.formats = LM49453_FORMATS,
},
.ops = &lm49453_headset_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "LM49453 Speaker",
@@ -1395,15 +1389,16 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_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),
- .idle_bias_off = true,
+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 = {
@@ -1416,8 +1411,7 @@ static const struct regmap_config lm49453_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int lm49453_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int lm49453_i2c_probe(struct i2c_client *i2c)
{
struct lm49453_priv *lm49453;
int ret = 0;
@@ -1438,23 +1432,17 @@ static int lm49453_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_lm49453,
+ 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 codec: %d\n", ret);
+ dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
return ret;
}
-static int lm49453_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id lm49453_i2c_id[] = {
- { "lm49453", 0 },
+ { "lm49453" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
@@ -1462,10 +1450,8 @@ MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
static struct i2c_driver lm49453_i2c_driver = {
.driver = {
.name = "lm49453",
- .owner = THIS_MODULE,
},
.probe = lm49453_i2c_probe,
- .remove = lm49453_i2c_remove,
.id_table = lm49453_i2c_id,
};
diff --git a/sound/soc/codecs/lm49453.h b/sound/soc/codecs/lm49453.h
index a63cfa5c0883..578a773e6fc9 100644
--- a/sound/soc/codecs/lm49453.h
+++ b/sound/soc/codecs/lm49453.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* lm49453.h - LM49453 ALSA Soc Audio drive
*
* Copyright (c) 2012 Texas Instruments, Inc
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
*/
#ifndef _LM49453_H
diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c
new file mode 100644
index 000000000000..a3d6318c9050
--- /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 <ckeepax@opensource.cirrus.com>
+// Piotr Stankiewicz <piotrs@opensource.cirrus.com>
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
+
+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_CBC_CFC);
+}
+
+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_CBP_CFP);
+}
+
+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 <piotrs@opensource.cirrus.com>");
+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 000000000000..6e3b8d0897dd
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, The Linux Foundation. All rights reserved.
+
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+#include "lpass-macro-common.h"
+
+static DEFINE_MUTEX(lpass_codec_mutex);
+static enum lpass_codec_version lpass_codec_version;
+
+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);
+
+void lpass_macro_set_codec_version(enum lpass_codec_version version)
+{
+ mutex_lock(&lpass_codec_mutex);
+ lpass_codec_version = version;
+ mutex_unlock(&lpass_codec_mutex);
+}
+EXPORT_SYMBOL_GPL(lpass_macro_set_codec_version);
+
+enum lpass_codec_version lpass_macro_get_codec_version(void)
+{
+ enum lpass_codec_version ver;
+
+ mutex_lock(&lpass_codec_mutex);
+ ver = lpass_codec_version;
+ mutex_unlock(&lpass_codec_mutex);
+
+ return ver;
+}
+EXPORT_SYMBOL_GPL(lpass_macro_get_codec_version);
+
+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 000000000000..10ad682019fa
--- /dev/null
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -0,0 +1,77 @@
+/* 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)
+/* The soundwire block should be internally reset at probe */
+#define LPASS_MACRO_FLAG_RESET_SWR BIT(1)
+
+enum lpass_version {
+ LPASS_VER_9_0_0,
+ LPASS_VER_9_2_0,
+ LPASS_VER_10_0_0,
+ LPASS_VER_11_0_0,
+};
+
+enum lpass_codec_version {
+ LPASS_CODEC_VERSION_UNKNOWN,
+ LPASS_CODEC_VERSION_1_0,
+ LPASS_CODEC_VERSION_1_1,
+ LPASS_CODEC_VERSION_1_2,
+ LPASS_CODEC_VERSION_2_0,
+ LPASS_CODEC_VERSION_2_1,
+ LPASS_CODEC_VERSION_2_5,
+ LPASS_CODEC_VERSION_2_6,
+ LPASS_CODEC_VERSION_2_7,
+ LPASS_CODEC_VERSION_2_8,
+ LPASS_CODEC_VERSION_2_9,
+};
+
+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);
+void lpass_macro_set_codec_version(enum lpass_codec_version version);
+enum lpass_codec_version lpass_macro_get_codec_version(void);
+
+static inline void lpass_macro_pds_exit_action(void *pds)
+{
+ lpass_macro_pds_exit(pds);
+}
+
+static inline const char *lpass_macro_get_codec_version_string(int version)
+{
+ switch (version) {
+ case LPASS_CODEC_VERSION_1_0:
+ return "v1.0";
+ case LPASS_CODEC_VERSION_1_1:
+ return "v1.1";
+ case LPASS_CODEC_VERSION_1_2:
+ return "v1.2";
+ case LPASS_CODEC_VERSION_2_0:
+ return "v2.0";
+ case LPASS_CODEC_VERSION_2_1:
+ return "v2.1";
+ case LPASS_CODEC_VERSION_2_5:
+ return "v2.5";
+ case LPASS_CODEC_VERSION_2_6:
+ return "v2.6";
+ case LPASS_CODEC_VERSION_2_7:
+ return "v2.7";
+ case LPASS_CODEC_VERSION_2_8:
+ return "v2.8";
+ default:
+ break;
+ }
+ return "NA";
+}
+
+#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 000000000000..a8fc842cc94e
--- /dev/null
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -0,0 +1,4039 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#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(rx, n) (0x0400 + rx->rxn_reg_stride * 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(rx, n) (0x0404 + rx->rxn_reg_stride * 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(rx, n) (0x0408 + rx->rxn_reg_stride * 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(rx, n) (0x040C + rx->rxn_reg_stride * 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(rx, n) (0x0410 + rx->rxn_reg_stride * 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(rx, n) (0x0414 + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_VOL_CTL (0x0414)
+#define CDC_RX_RXn_RX_PATH_MIX_CTL(rx, n) (0x0418 + rx->rxn_reg_stride * 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(rx, n) (0x0420 + rx->rxn_reg_stride * 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_RXn_RX_PATH_SEC3(rx, n) (0x042c + rx->rxn_reg_stride * n)
+#define CDC_RX_RX0_RX_PATH_SEC4 (0x0430)
+#define CDC_RX_RX0_RX_PATH_SEC7 (0x0434)
+#define CDC_RX_RXn_RX_PATH_SEC7(rx, n) \
+ (0x0434 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#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(rx, n) \
+ (0x0440 + (rx->rxn_reg_stride * n) + ((n > 1) ? rx->rxn_reg_stride2 : 0))
+#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)
+/* RX offsets prior to 2.5 codec version */
+#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)
+
+/* LPASS CODEC version 2.5 rx reg offsets */
+#define CDC_2_5_RX_RX1_RX_PATH_CTL (0x04c0)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG0 (0x04c4)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG1 (0x04c8)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG2 (0x04cC)
+#define CDC_2_5_RX_RX1_RX_PATH_CFG3 (0x04d0)
+#define CDC_2_5_RX_RX1_RX_VOL_CTL (0x04d4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CTL (0x04d8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_CFG (0x04dC)
+#define CDC_2_5_RX_RX1_RX_VOL_MIX_CTL (0x04e0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC1 (0x04e4)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC2 (0x04e8)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC3 (0x04eC)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC4 (0x04f0)
+#define CDC_2_5_RX_RX1_RX_PATH_SEC7 (0x04f4)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0 (0x04f8)
+#define CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1 (0x04fC)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_CTL (0x0500)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1 (0x0504)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2 (0x0508)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3 (0x050C)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4 (0x0510)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5 (0x0514)
+#define CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6 (0x0518)
+
+#define CDC_2_5_RX_RX2_RX_PATH_CTL (0x0580)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG0 (0x0584)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG1 (0x0588)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG2 (0x058C)
+#define CDC_2_5_RX_RX2_RX_PATH_CFG3 (0x0590)
+#define CDC_2_5_RX_RX2_RX_VOL_CTL (0x0594)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CTL (0x0598)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_CFG (0x059C)
+#define CDC_2_5_RX_RX2_RX_VOL_MIX_CTL (0x05a0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC0 (0x05a4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC1 (0x05a8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC2 (0x05aC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC3 (0x05b0)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC4 (0x05b4)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC5 (0x05b8)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC6 (0x05bC)
+#define CDC_2_5_RX_RX2_RX_PATH_SEC7 (0x05c0)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0 (0x05c4)
+#define CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1 (0x05c8)
+#define CDC_2_5_RX_RX2_RX_PATH_DSM_CTL (0x05cC)
+
+#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},
+ },
+};
+
+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},
+};
+
+/* Matches also rx_macro_mux_text */
+enum {
+ 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;
+ enum lpass_codec_version codec_version;
+ int rxn_reg_stride;
+ int rxn_reg_stride2;
+ 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",
+};
+
+/* Order must match RX_MACRO_MAX_DAIS enum (offset by 1) */
+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_2_5_int1_dem_inp_enum, CDC_2_5_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_PROC2, 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_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 const struct reg_default rx_2_5_defaults[] = {
+ { CDC_2_5_RX_RX1_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX1_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX1_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_CTL, 0x08 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3, 0x00 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5, 0x55 },
+ { CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6, 0x55 },
+ { CDC_2_5_RX_RX2_RX_PATH_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG0, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG1, 0x64 },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG2, 0x8F },
+ { CDC_2_5_RX_RX2_RX_PATH_CFG3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_VOL_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CTL, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_CFG, 0x7E },
+ { CDC_2_5_RX_RX2_RX_VOL_MIX_CTL, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC0, 0x04 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC1, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC2, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC3, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC4, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC5, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC6, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_SEC7, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0, 0x08 },
+ { CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1, 0x00 },
+ { CDC_2_5_RX_RX2_RX_PATH_DSM_CTL, 0x00 },
+};
+
+static const struct reg_default rx_pre_2_5_defaults[] = {
+ { 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 },
+
+};
+
+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_pre_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_2_5_is_rw_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_RX_RX1_RX_PATH_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX1_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX1_RX_VOL_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX1_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX1_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX1_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_CTL:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA1:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA2:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA3:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA4:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA5:
+ case CDC_2_5_RX_RX1_RX_PATH_DSM_DATA6:
+ case CDC_2_5_RX_RX2_RX_PATH_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG0:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG1:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG2:
+ case CDC_2_5_RX_RX2_RX_PATH_CFG3:
+ case CDC_2_5_RX_RX2_RX_VOL_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_CFG:
+ case CDC_2_5_RX_RX2_RX_VOL_MIX_CTL:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC2:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC3:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC4:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC5:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC6:
+ case CDC_2_5_RX_RX2_RX_PATH_SEC7:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC0:
+ case CDC_2_5_RX_RX2_RX_PATH_MIX_SEC1:
+ case CDC_2_5_RX_RX2_RX_PATH_DSM_CTL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool rx_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ return rx_pre_2_5_is_rw_register(dev, reg);
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ return rx_2_5_is_rw_register(dev, reg);
+ default:
+ break;
+ }
+
+ 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,
+ .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 rx_macro *rx = snd_soc_component_get_drvdata(component);
+ 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_RXn_RX_PATH_CFG1(rx, 0))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ else if (e->reg == CDC_RX_RXn_RX_PATH_CFG1(rx, 1))
+ look_ahead_dly_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+
+ /* 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 const struct snd_kcontrol_new rx_2_5_int1_dem_inp_mux =
+ SOC_DAPM_ENUM_EXT("rx_int1_dem_inp", rx_2_5_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(rx, 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(rx, 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(const 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;
+ struct rx_macro *rx = snd_soc_component_get_drvdata(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(rx, j);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, j);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, 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);
+ }
+
+ 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, reg;
+
+ reg = CDC_RX_RXn_RX_PATH_CTL(rx, w->shift);
+ gain_reg = CDC_RX_RXn_RX_VOL_CTL(rx, 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(rx, 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(rx, 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(rx, 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(rx, 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(rx, 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_RXn_RX_PATH_CFG1(rx, 2), 0x04, 0x00);
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Reset to default (HPF=ON) */
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG1(rx, 2), 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_RXn_RX_PATH_CFG0(rx, 0),
+ 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_RXn_RX_PATH_CFG0(rx, 1),
+ CDC_RX_RXn_CLSH_EN_MASK, 0x1);
+ break;
+ case INTERP_AUX:
+ snd_soc_component_update_bits(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ CDC_RX_RX2_DLY_Z_EN_MASK, 1);
+ snd_soc_component_write_field(component,
+ CDC_RX_RXn_RX_PATH_CFG0(rx, 2),
+ 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)
+{
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 hd2_scale_reg, hd2_enable_reg;
+
+ switch (interp_idx) {
+ case INTERP_HPHL:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 0);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 0);
+ break;
+ case INTERP_HPHR:
+ hd2_scale_reg = CDC_RX_RXn_RX_PATH_SEC3(rx, 1);
+ hd2_enable_reg = CDC_RX_RXn_RX_PATH_CFG0(rx, 1);
+ 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];
+ unsigned int dai_id;
+ 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:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (RX_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (rx->active_ch_cnt[dai_id]) {
+ clear_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(widget->shift, &rx->active_ch_mask[dai_id]);
+ rx->active_ch_cnt[dai_id]++;
+ 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_RXn_RX_PATH_CFG1(rx, 0),
+ 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_RXn_RX_PATH_CFG1(rx, 0),
+ 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(rx, interp_idx);
+ dsm_reg = CDC_RX_RXn_RX_PATH_DSM_CTL(rx, interp_idx);
+ rx_cfg2_reg = CDC_RX_RXn_RX_PATH_CFG2(rx, 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ u16 gain_reg, mix_reg;
+
+ gain_reg = CDC_RX_RXn_RX_VOL_MIX_CTL(rx, w->shift);
+ mix_reg = CDC_RX_RXn_RX_PATH_MIX_CTL(rx, 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);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+
+ 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(rx, w->shift),
+ CDC_RX_RXn_SIDETONE_EN_MASK, 1);
+ snd_soc_component_write_field(component, CDC_RX_RXn_RX_PATH_CTL(rx, 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(rx, 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_def_snd_controls[] = {
+ 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_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),
+};
+
+static const struct snd_kcontrol_new rx_macro_2_5_snd_controls[] = {
+
+ SOC_SINGLE_S8_TLV("RX_RX1 Digital Volume", CDC_2_5_RX_RX1_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Digital Volume", CDC_2_5_RX_RX2_RX_VOL_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX1 Mix Digital Volume", CDC_2_5_RX_RX1_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+ SOC_SINGLE_S8_TLV("RX_RX2 Mix Digital Volume", CDC_2_5_RX_RX2_RX_VOL_MIX_CTL,
+ -84, 40, digital_gain),
+};
+
+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_RX0 Mix Digital Volume", CDC_RX_RX0_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 (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX0 MUX")))
+ ec_tx = ((val & 0xf0) >> 0x4) - 1;
+ else if (!(snd_soc_dapm_widget_name_cmp(w, "RX MIX TX1 MUX")))
+ ec_tx = (val & 0x0f) - 1;
+
+ val = snd_soc_component_read(component,
+ CDC_RX_INP_MUX_RX_MIX_CFG5);
+ if (!(snd_soc_dapm_widget_name_cmp(w, "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_2_5_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_2_5_int1_dem_inp_mux),
+};
+
+static const struct snd_soc_dapm_widget rx_macro_def_dapm_widgets[] = {
+ SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+ &rx_int1_dem_inp_mux),
+};
+
+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_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 snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct rx_macro *rx = snd_soc_component_get_drvdata(component);
+ const struct snd_soc_dapm_widget *widgets;
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls, num_widgets;
+ int ret;
+
+ snd_soc_component_init_regmap(component, rx->regmap);
+
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 0),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 1),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_SEC7(rx, 2),
+ CDC_RX_DSM_OUT_DELAY_SEL_MASK,
+ CDC_RX_DSM_OUT_DELAY_TWO_SAMPLE);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 0),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 1),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+ snd_soc_component_update_bits(component, CDC_RX_RXn_RX_PATH_CFG3(rx, 2),
+ CDC_RX_DC_COEFF_SEL_MASK,
+ CDC_RX_DC_COEFF_SEL_TWO);
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ controls = rx_macro_def_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_def_snd_controls);
+ widgets = rx_macro_def_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_def_dapm_widgets);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ controls = rx_macro_2_5_snd_controls;
+ num_controls = ARRAY_SIZE(rx_macro_2_5_snd_controls);
+ widgets = rx_macro_2_5_dapm_widgets;
+ num_widgets = ARRAY_SIZE(rx_macro_2_5_dapm_widgets);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ rx->component = component;
+
+ ret = snd_soc_add_component_controls(component, controls, num_controls);
+ if (ret)
+ return ret;
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+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, def_count;
+
+ 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);
+
+ ret = devm_add_action_or_reset(dev, lpass_macro_pds_exit_action, rx->pds);
+ if (ret)
+ return ret;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rx->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (rx->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ rx->rxn_reg_stride = 0x80;
+ rx->rxn_reg_stride2 = 0xc;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_pre_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_pre_2_5_defaults, sizeof(rx_pre_2_5_defaults));
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ rx->rxn_reg_stride = 0xc0;
+ rx->rxn_reg_stride2 = 0x0;
+ def_count = ARRAY_SIZE(rx_defaults) + ARRAY_SIZE(rx_2_5_defaults);
+ reg_defaults = kmalloc_array(def_count, sizeof(struct reg_default), GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], rx_defaults, sizeof(rx_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(rx_defaults)],
+ rx_2_5_defaults, sizeof(rx_2_5_defaults));
+ break;
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", rx->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ rx->regmap = devm_regmap_init_mmio(dev, base, reg_config);
+ if (IS_ERR(rx->regmap))
+ return PTR_ERR(rx->regmap);
+
+ 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)
+ return ret;
+
+ 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);
+
+ 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);
+}
+
+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 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 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 = {
+ 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 = pm_ptr(&rx_macro_pm_ops),
+ },
+ .probe = rx_macro_probe,
+ .remove = 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 000000000000..1da34cb3505f
--- /dev/null
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -0,0 +1,2542 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+
+#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)
+/* Default divider for AMIC and DMIC clock: DIV2 */
+#define CDC_TX_SWR_MIC_CLK_DEFAULT 0
+#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_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_data {
+ unsigned int flags;
+ unsigned int ver;
+ const struct snd_soc_dapm_widget *extra_widgets;
+ size_t extra_widgets_num;
+ const struct snd_soc_dapm_route *extra_routes;
+ size_t extra_routes_num;
+};
+
+struct tx_macro {
+ struct device *dev;
+ const struct tx_macro_data *data;
+ 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;
+ 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:
+ case CDC_TX_TOP_CSR_SWR_AMIC0_CTL:
+ case CDC_TX_TOP_CSR_SWR_AMIC1_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,
+ struct tx_macro *tx, 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) {
+ if (tx->data->ver > LPASS_VER_9_0_0)
+ return true;
+
+ /* else: LPASS <= v9.0.0 */
+ 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, tx, 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 void tx_macro_update_smic_sel_v9(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+}
+
+static void tx_macro_update_smic_sel_v9_2(struct snd_soc_component *component,
+ struct snd_soc_dapm_widget *widget,
+ struct tx_macro *tx, u16 mic_sel_reg,
+ unsigned int val)
+{
+ unsigned int dmic;
+ u16 dmic_clk_reg;
+
+ if (widget->shift) {
+ /* MSM DMIC */
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ } else {
+ snd_soc_component_write_field(component, mic_sel_reg,
+ CDC_TXn_ADC_DMIC_SEL_MASK, 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;
+ struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ u16 mic_sel_reg;
+
+ 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 (tx->data->ver <= LPASS_VER_9_0_0)
+ tx_macro_update_smic_sel_v9(component, widget, tx,
+ mic_sel_reg, val);
+ else
+ tx_macro_update_smic_sel_v9_2(component, widget, tx,
+ mic_sel_reg, val);
+ }
+
+ 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,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ }
+ }
+ 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, tx, 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, tx, 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, tx, 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, tx, 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(const 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 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 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 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 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 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 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 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 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 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 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"},
+};
+
+/* Controls and routes specific to LPASS <= v9.0.0 */
+static const char * const smic_mux_text_v9[] = {
+ "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_v9, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9),
+
+ 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"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9[] = {
+ {"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", "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", "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", "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", "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", "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", "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", "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"},
+};
+
+/* Controls and routes specific to LPASS >= v9.2.0 */
+static const char * const smic_mux_text_v9_2[] = {
+ "ZERO", "SWR_MIC0", "SWR_MIC1", "SWR_MIC2", "SWR_MIC3",
+ "SWR_MIC4", "SWR_MIC5", "SWR_MIC6", "SWR_MIC7",
+ "SWR_MIC8", "SWR_MIC9", "SWR_MIC10", "SWR_MIC11"
+};
+
+static SOC_ENUM_SINGLE_DECL(tx_smic0_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX0_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic1_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX1_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic2_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX2_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic3_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX3_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic4_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX4_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic5_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX5_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic6_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX6_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static SOC_ENUM_SINGLE_DECL(tx_smic7_enum_v9_2, CDC_TX_INP_MUX_ADC_MUX7_CFG0,
+ 0, smic_mux_text_v9_2);
+
+static const struct snd_kcontrol_new tx_smic0_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic0", tx_smic0_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic1_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic1", tx_smic1_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic2_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic2", tx_smic2_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic3_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic3", tx_smic3_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic4_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic4", tx_smic4_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic5_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic5", tx_smic5_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic6_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic6", tx_smic6_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+static const struct snd_kcontrol_new tx_smic7_mux_v9_2 = SOC_DAPM_ENUM_EXT("tx_smic7", tx_smic7_enum_v9_2,
+ snd_soc_dapm_get_enum_double, tx_macro_put_dec_enum);
+
+static const struct snd_soc_dapm_widget tx_macro_dapm_widgets_v9_2[] = {
+ SND_SOC_DAPM_MUX("TX SMIC MUX0", SND_SOC_NOPM, 0, 0, &tx_smic0_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX1", SND_SOC_NOPM, 0, 0, &tx_smic1_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX2", SND_SOC_NOPM, 0, 0, &tx_smic2_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX3", SND_SOC_NOPM, 0, 0, &tx_smic3_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX4", SND_SOC_NOPM, 0, 0, &tx_smic4_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX5", SND_SOC_NOPM, 0, 0, &tx_smic5_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX6", SND_SOC_NOPM, 0, 0, &tx_smic6_mux_v9_2),
+ SND_SOC_DAPM_MUX("TX SMIC MUX7", SND_SOC_NOPM, 0, 0, &tx_smic7_mux_v9_2),
+
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT0"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT1"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT2"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT3"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT4"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT5"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT6"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT7"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT8"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT9"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT10"),
+ SND_SOC_DAPM_INPUT("TX SWR_INPUT11"),
+};
+
+static const struct snd_soc_dapm_route tx_audio_map_v9_2[] = {
+ {"TX DEC0 MUX", "SWR_MIC", "TX SMIC MUX0"},
+ {"TX SMIC MUX0", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX0", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX0", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX0", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX0", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX0", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX0", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX0", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX0", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX0", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX0", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX0", "SWR_MIC10", "TX SWR_INPUT11"},
+ {"TX SMIC MUX0", "SWR_MIC11", "TX SWR_INPUT10"},
+
+ {"TX DEC1 MUX", "SWR_MIC", "TX SMIC MUX1"},
+ {"TX SMIC MUX1", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX1", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX1", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX1", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX1", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX1", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX1", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX1", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX1", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX1", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX1", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX1", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX1", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC2 MUX", "SWR_MIC", "TX SMIC MUX2"},
+ {"TX SMIC MUX2", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX2", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX2", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX2", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX2", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX2", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX2", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX2", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX2", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX2", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX2", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX2", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX2", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC3 MUX", "SWR_MIC", "TX SMIC MUX3"},
+ {"TX SMIC MUX3", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX3", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX3", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX3", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX3", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX3", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX3", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX3", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX3", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX3", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX3", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX3", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX3", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC4 MUX", "SWR_MIC", "TX SMIC MUX4"},
+ {"TX SMIC MUX4", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX4", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX4", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX4", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX4", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX4", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX4", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX4", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX4", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX4", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX4", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX4", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX4", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC5 MUX", "SWR_MIC", "TX SMIC MUX5"},
+ {"TX SMIC MUX5", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX5", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX5", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX5", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX5", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX5", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX5", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX5", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX5", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX5", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX5", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX5", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX5", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC6 MUX", "SWR_MIC", "TX SMIC MUX6"},
+ {"TX SMIC MUX6", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX6", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX6", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX6", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX6", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX6", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX6", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX6", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX6", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX6", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX6", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX6", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX6", "SWR_MIC11", "TX SWR_INPUT11"},
+
+ {"TX DEC7 MUX", "SWR_MIC", "TX SMIC MUX7"},
+ {"TX SMIC MUX7", NULL, "TX_SWR_CLK"},
+ {"TX SMIC MUX7", "SWR_MIC0", "TX SWR_INPUT0"},
+ {"TX SMIC MUX7", "SWR_MIC1", "TX SWR_INPUT1"},
+ {"TX SMIC MUX7", "SWR_MIC2", "TX SWR_INPUT2"},
+ {"TX SMIC MUX7", "SWR_MIC3", "TX SWR_INPUT3"},
+ {"TX SMIC MUX7", "SWR_MIC4", "TX SWR_INPUT4"},
+ {"TX SMIC MUX7", "SWR_MIC5", "TX SWR_INPUT5"},
+ {"TX SMIC MUX7", "SWR_MIC6", "TX SWR_INPUT6"},
+ {"TX SMIC MUX7", "SWR_MIC7", "TX SWR_INPUT7"},
+ {"TX SMIC MUX7", "SWR_MIC8", "TX SWR_INPUT8"},
+ {"TX SMIC MUX7", "SWR_MIC9", "TX SWR_INPUT9"},
+ {"TX SMIC MUX7", "SWR_MIC10", "TX SWR_INPUT10"},
+ {"TX SMIC MUX7", "SWR_MIC11", "TX SWR_INPUT11"},
+};
+
+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_extend(struct snd_soc_component *comp)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp);
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int ret;
+
+ if (tx->data->extra_widgets_num) {
+ ret = snd_soc_dapm_new_controls(dapm, tx->data->extra_widgets,
+ tx->data->extra_widgets_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra widgets: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (tx->data->extra_routes_num) {
+ ret = snd_soc_dapm_add_routes(dapm, tx->data->extra_routes,
+ tx->data->extra_routes_num);
+ if (ret) {
+ dev_err(tx->dev, "failed to add extra routes: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tx_macro_component_probe(struct snd_soc_component *comp)
+{
+ struct tx_macro *tx = snd_soc_component_get_drvdata(comp);
+ int i, ret;
+
+ ret = tx_macro_component_extend(comp);
+ if (ret)
+ return ret;
+
+ 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_write(comp, CDC_TX_TOP_CSR_SWR_AMIC0_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+ snd_soc_component_write(comp, CDC_TX_TOP_CSR_SWR_AMIC1_CTL,
+ CDC_TX_SWR_MIC_CLK_DEFAULT);
+
+ 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 = "TX-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;
+ struct tx_macro *tx;
+ void __iomem *base;
+ int ret, reg;
+
+ tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
+ if (!tx)
+ return -ENOMEM;
+
+ tx->data = device_get_match_data(dev);
+
+ 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 (tx->data->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 */
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ 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);
+
+ if (tx->data->flags & LPASS_MACRO_FLAG_RESET_SWR)
+ 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 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 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 = {
+ RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
+};
+
+static const struct tx_macro_data lpass_ver_9 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9),
+ .extra_routes = tx_audio_map_v9,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9),
+};
+
+static const struct tx_macro_data lpass_ver_9_2 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK |
+ LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_9_2_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_10_sm6115 = {
+ .flags = LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ .ver = LPASS_VER_10_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct tx_macro_data lpass_ver_11 = {
+ .flags = LPASS_MACRO_FLAG_RESET_SWR,
+ .ver = LPASS_VER_11_0_0,
+ .extra_widgets = tx_macro_dapm_widgets_v9_2,
+ .extra_widgets_num = ARRAY_SIZE(tx_macro_dapm_widgets_v9_2),
+ .extra_routes = tx_audio_map_v9_2,
+ .extra_routes_num = ARRAY_SIZE(tx_audio_map_v9_2),
+};
+
+static const struct of_device_id tx_macro_dt_match[] = {
+ {
+ /*
+ * The block is actually LPASS v9.4, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC7280 fine, so apparently the fix
+ * is not necessary.
+ */
+ .compatible = "qcom,sc7280-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm6115-lpass-tx-macro",
+ .data = &lpass_ver_10_sm6115,
+ }, {
+ .compatible = "qcom,sm8250-lpass-tx-macro",
+ .data = &lpass_ver_9,
+ }, {
+ .compatible = "qcom,sm8450-lpass-tx-macro",
+ .data = &lpass_ver_9_2,
+ }, {
+ .compatible = "qcom,sm8550-lpass-tx-macro",
+ .data = &lpass_ver_11,
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-tx-macro",
+ /*
+ * The block is actually LPASS v9.3, but keep LPASS v9 match
+ * data and audio widgets, due to compatibility reasons.
+ * Microphones are working on SC8280xp fine, so apparently the
+ * fix is not necessary.
+ */
+ .data = &lpass_ver_9,
+ },
+ { }
+};
+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 = pm_ptr(&tx_macro_pm_ops),
+ },
+ .probe = tx_macro_probe,
+ .remove = 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 000000000000..2e1b77973a3e
--- /dev/null
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -0,0 +1,1747 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_clk.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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_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;
+ bool has_npl_clk;
+
+ int dec_mode[VA_MACRO_NUM_DECIMATORS];
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct clk *npl;
+ 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;
+ bool has_npl_clk;
+ int version;
+};
+
+static const struct va_macro_data sm8250_va_data = {
+ .has_swr_master = false,
+ .has_npl_clk = false,
+ .version = LPASS_CODEC_VERSION_1_0,
+};
+
+static const struct va_macro_data sm8450_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = true,
+};
+
+static const struct va_macro_data sm8550_va_data = {
+ .has_swr_master = true,
+ .has_npl_clk = false,
+};
+
+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(const 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;
+
+ if (va->has_swr_master) {
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ return 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);
+ if (va->has_swr_master)
+ clk_disable_unprepare(va->mclk);
+}
+
+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;
+
+ if (va->has_npl_clk)
+ parent = va->npl;
+
+ 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 void va_macro_set_lpass_codec_version(struct va_macro *va)
+{
+ int core_id_0 = 0, core_id_1 = 0, core_id_2 = 0;
+ int version = LPASS_CODEC_VERSION_UNKNOWN;
+
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_0, &core_id_0);
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_1, &core_id_1);
+ regmap_read(va->regmap, CDC_VA_TOP_CSR_CORE_ID_2, &core_id_2);
+
+ if ((core_id_0 == 0x01) && (core_id_1 == 0x0F))
+ version = LPASS_CODEC_VERSION_2_0;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && core_id_2 == 0x01)
+ version = LPASS_CODEC_VERSION_2_0;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0E))
+ version = LPASS_CODEC_VERSION_2_1;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x50 || core_id_2 == 0x51))
+ version = LPASS_CODEC_VERSION_2_5;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x60 || core_id_2 == 0x61))
+ version = LPASS_CODEC_VERSION_2_6;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x70 || core_id_2 == 0x71))
+ version = LPASS_CODEC_VERSION_2_7;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x80 || core_id_2 == 0x81))
+ version = LPASS_CODEC_VERSION_2_8;
+ if ((core_id_0 == 0x02) && (core_id_1 == 0x0F) && (core_id_2 == 0x90 || core_id_2 == 0x91))
+ version = LPASS_CODEC_VERSION_2_9;
+
+ if (version == LPASS_CODEC_VERSION_UNKNOWN)
+ dev_warn(va->dev, "Unknown Codec version, ID: %02x / %02x / %02x\n",
+ core_id_0, core_id_1, core_id_2);
+
+ lpass_macro_set_codec_version(version);
+
+ dev_dbg(va->dev, "LPASS Codec Version %s\n", lpass_macro_get_codec_version_string(version));
+}
+
+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;
+ va->has_npl_clk = data->has_npl_clk;
+
+ /* mclk rate */
+ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
+
+ if (va->has_npl_clk) {
+ va->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(va->npl)) {
+ ret = PTR_ERR(va->npl);
+ goto err;
+ }
+
+ clk_set_rate(va->npl, 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_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret)
+ goto err_npl;
+ }
+
+ /**
+ * old version of codecs do not have a reliable way to determine the
+ * version from registers, get them from soc specific data
+ */
+ if (data->version)
+ lpass_macro_set_codec_version(data->version);
+ else /* read version from register */
+ va_macro_set_lpass_codec_version(va);
+
+ 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:
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+err_npl:
+ 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);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+ clk_disable_unprepare(va->dcodec);
+ clk_disable_unprepare(va->macro);
+
+ lpass_macro_pds_exit(va->pds);
+}
+
+static int 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);
+
+ if (va->has_npl_clk)
+ clk_disable_unprepare(va->npl);
+
+ clk_disable_unprepare(va->mclk);
+
+ return 0;
+}
+
+static int 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;
+ }
+
+ if (va->has_npl_clk) {
+ ret = clk_prepare_enable(va->npl);
+ if (ret) {
+ clk_disable_unprepare(va->mclk);
+ dev_err(va->dev, "unable to prepare npl\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 = {
+ 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,sm8550-lpass-va-macro", .data = &sm8550_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 = pm_ptr(&va_macro_pm_ops),
+ },
+ .probe = va_macro_probe,
+ .remove = 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 000000000000..38faa9074ca3
--- /dev/null
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -0,0 +1,3048 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
+
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of_clk.h>
+#include <linux/clk-provider.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <sound/tlv.h>
+
+#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_INP_MUX_RX_INT0_CFG1 (0x0104)
+#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_TX_SPKR_PROT_PCM_RATE_16K 1
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K 2
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K 3
+#define CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K 4
+#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)
+/* CDC_WSA_COMPANDER1_CTLx and CDC_WSA_SOFTCLIPx differ per LPASS codec versions */
+#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)
+
+/* LPASS codec version <=2.4 register offsets */
+#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)
+
+/* LPASS codec version >=2.5 register offsets */
+#define CDC_WSA_TOP_FS_UNGATE (0x00AC)
+#define CDC_WSA_TOP_GRP_SEL (0x00B0)
+#define CDC_WSA_TOP_FS_UNGATE2 (0x00DC)
+#define CDC_2_5_WSA_COMPANDER0_CTL8 (0x05A0)
+#define CDC_2_5_WSA_COMPANDER0_CTL9 (0x05A4)
+#define CDC_2_5_WSA_COMPANDER0_CTL10 (0x05A8)
+#define CDC_2_5_WSA_COMPANDER0_CTL11 (0x05AC)
+#define CDC_2_5_WSA_COMPANDER0_CTL12 (0x05B0)
+#define CDC_2_5_WSA_COMPANDER0_CTL13 (0x05B4)
+#define CDC_2_5_WSA_COMPANDER0_CTL14 (0x05B8)
+#define CDC_2_5_WSA_COMPANDER0_CTL15 (0x05BC)
+#define CDC_2_5_WSA_COMPANDER0_CTL16 (0x05C0)
+#define CDC_2_5_WSA_COMPANDER0_CTL17 (0x05C4)
+#define CDC_2_5_WSA_COMPANDER0_CTL18 (0x05C8)
+#define CDC_2_5_WSA_COMPANDER0_CTL19 (0x05CC)
+#define CDC_2_5_WSA_COMPANDER1_CTL0 (0x05E0)
+#define CDC_2_5_WSA_COMPANDER1_CTL1 (0x05E4)
+#define CDC_2_5_WSA_COMPANDER1_CTL2 (0x05E8)
+#define CDC_2_5_WSA_COMPANDER1_CTL3 (0x05EC)
+#define CDC_2_5_WSA_COMPANDER1_CTL4 (0x05F0)
+#define CDC_2_5_WSA_COMPANDER1_CTL5 (0x05F4)
+#define CDC_2_5_WSA_COMPANDER1_CTL6 (0x05F8)
+#define CDC_2_5_WSA_COMPANDER1_CTL7 (0x05FC)
+#define CDC_2_5_WSA_COMPANDER1_CTL8 (0x0600)
+#define CDC_2_5_WSA_COMPANDER1_CTL9 (0x0604)
+#define CDC_2_5_WSA_COMPANDER1_CTL10 (0x0608)
+#define CDC_2_5_WSA_COMPANDER1_CTL11 (0x060C)
+#define CDC_2_5_WSA_COMPANDER1_CTL12 (0x0610)
+#define CDC_2_5_WSA_COMPANDER1_CTL13 (0x0614)
+#define CDC_2_5_WSA_COMPANDER1_CTL14 (0x0618)
+#define CDC_2_5_WSA_COMPANDER1_CTL15 (0x061C)
+#define CDC_2_5_WSA_COMPANDER1_CTL16 (0x0620)
+#define CDC_2_5_WSA_COMPANDER1_CTL17 (0x0624)
+#define CDC_2_5_WSA_COMPANDER1_CTL18 (0x0628)
+#define CDC_2_5_WSA_COMPANDER1_CTL19 (0x062C)
+#define CDC_2_5_WSA_SOFTCLIP0_CRC (0x0640)
+#define CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL (0x0644)
+#define CDC_2_5_WSA_SOFTCLIP1_CRC (0x0660)
+#define CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL (0x0664)
+
+#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_CFG_OFFSET 0x8
+#define WSA_MACRO_MUX_CFG1_OFFSET 0x4
+#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 */
+};
+
+/* Matches also rx_mux_text */
+enum {
+ WSA_MACRO_AIF1_PB,
+ WSA_MACRO_AIF_MIX1_PB,
+ WSA_MACRO_AIF_VI,
+ WSA_MACRO_AIF_ECHO,
+ WSA_MACRO_MAX_DAIS,
+};
+
+/**
+ * struct wsa_reg_layout - Register layout differences
+ * @rx_intx_1_mix_inp0_sel_mask: register mask for RX_INTX_1_MIX_INP0_SEL_MASK
+ * @rx_intx_1_mix_inp1_sel_mask: register mask for RX_INTX_1_MIX_INP1_SEL_MASK
+ * @rx_intx_1_mix_inp2_sel_mask: register mask for RX_INTX_1_MIX_INP2_SEL_MASK
+ * @rx_intx_2_sel_mask: register mask for RX_INTX_2_SEL_MASK
+ * @compander1_reg_offset: offset between compander registers (compander1 - compander0)
+ * @softclip0_reg_base: base address of softclip0 register
+ * @softclip1_reg_offset: offset between compander registers (softclip1 - softclip0)
+ */
+struct wsa_reg_layout {
+ unsigned int rx_intx_1_mix_inp0_sel_mask;
+ unsigned int rx_intx_1_mix_inp1_sel_mask;
+ unsigned int rx_intx_1_mix_inp2_sel_mask;
+ unsigned int rx_intx_2_sel_mask;
+ unsigned int compander1_reg_offset;
+ unsigned int softclip0_reg_base;
+ unsigned int softclip1_reg_offset;
+};
+
+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;
+ enum lpass_codec_version codec_version;
+ const struct wsa_reg_layout *reg_layout;
+ 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;
+ u32 pcm_rate_vi;
+ 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 struct wsa_reg_layout wsa_codec_v2_1 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(2, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(5, 3),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(5, 3),
+ .rx_intx_2_sel_mask = GENMASK(2, 0),
+ .compander1_reg_offset = 0x40,
+ .softclip0_reg_base = 0x600,
+ .softclip1_reg_offset = 0x40,
+};
+
+static const struct wsa_reg_layout wsa_codec_v2_5 = {
+ .rx_intx_1_mix_inp0_sel_mask = GENMASK(3, 0),
+ .rx_intx_1_mix_inp1_sel_mask = GENMASK(7, 4),
+ .rx_intx_1_mix_inp2_sel_mask = GENMASK(7, 4),
+ .rx_intx_2_sel_mask = GENMASK(3, 0),
+ .compander1_reg_offset = 0x60,
+ .softclip0_reg_base = 0x640,
+ .softclip1_reg_offset = 0x20,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
+
+static const char *const rx_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "DEC0", "DEC1"
+};
+
+static const char *const rx_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8", "DEC0", "DEC1"
+};
+
+static const char *const rx_mix_text_v2_1[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1"
+};
+
+static const char *const rx_mix_text_v2_5[] = {
+ "ZERO", "RX0", "RX1", "RX_MIX0", "RX_MIX1", "RX4", "RX5", "RX6", "RX7", "RX8"
+};
+
+static const char *const rx_mix_ec_text[] = {
+ "ZERO", "RX_MIX_TX0", "RX_MIX_TX1"
+};
+
+/* Order must match WSA_MACRO_MAX_DAIS enum (offset by 1) */
+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_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx0_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx0_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT0_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+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_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx0_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP0 Mux", rx0_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP1 Mux", rx0_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 INP2 Mux", rx0_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx0_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX0 MIX Mux", rx0_mix_chain_enum_v2_5);
+
+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_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 3, 7, rx_text_v2_1);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_1 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 5, rx_mix_text_v2_1);
+
+static const struct soc_enum rx1_prim_inp0_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 0, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp1_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG0,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_prim_inp2_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 4, 12, rx_text_v2_5);
+
+static const struct soc_enum rx1_mix_chain_enum_v2_5 =
+ SOC_ENUM_SINGLE(CDC_WSA_RX_INP_MUX_RX_INT1_CFG1,
+ 0, 10, rx_mix_text_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_1 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_1);
+
+static const struct snd_kcontrol_new rx1_prim_inp0_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP0 Mux", rx1_prim_inp0_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp1_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP1 Mux", rx1_prim_inp1_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_prim_inp2_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 INP2 Mux", rx1_prim_inp2_chain_enum_v2_5);
+
+static const struct snd_kcontrol_new rx1_mix_mux_v2_5 =
+ SOC_DAPM_ENUM("WSA_RX1 MIX Mux", rx1_mix_chain_enum_v2_5);
+
+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_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_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 const struct reg_default wsa_defaults_v2_1[] = {
+ { 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_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},
+};
+
+static const struct reg_default wsa_defaults_v2_5[] = {
+ { CDC_WSA_TOP_FS_UNGATE, 0xFF},
+ { CDC_WSA_TOP_GRP_SEL, 0x08},
+ { CDC_WSA_TOP_FS_UNGATE2, 0x1F},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX0_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX1_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX2_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CTL, 0x04},
+ { CDC_WSA_TX3_SPKR_PROT_PATH_CFG0, 0x02},
+ { CDC_2_5_WSA_COMPANDER0_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER0_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER0_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER0_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER0_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER0_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER0_CTL19, 0x16},
+ { CDC_2_5_WSA_COMPANDER1_CTL0, 0x60},
+ { CDC_2_5_WSA_COMPANDER1_CTL1, 0xDB},
+ { CDC_2_5_WSA_COMPANDER1_CTL2, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL3, 0x35},
+ { CDC_2_5_WSA_COMPANDER1_CTL4, 0xFF},
+ { CDC_2_5_WSA_COMPANDER1_CTL5, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL6, 0x01},
+ { CDC_2_5_WSA_COMPANDER1_CTL7, 0x28},
+ { CDC_2_5_WSA_COMPANDER1_CTL8, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL9, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL10, 0x06},
+ { CDC_2_5_WSA_COMPANDER1_CTL11, 0x12},
+ { CDC_2_5_WSA_COMPANDER1_CTL12, 0x1E},
+ { CDC_2_5_WSA_COMPANDER1_CTL13, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL14, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL15, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL16, 0x00},
+ { CDC_2_5_WSA_COMPANDER1_CTL17, 0x24},
+ { CDC_2_5_WSA_COMPANDER1_CTL18, 0x2A},
+ { CDC_2_5_WSA_COMPANDER1_CTL19, 0x16},
+ { CDC_2_5_WSA_SOFTCLIP0_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL, 0x38},
+ { CDC_2_5_WSA_SOFTCLIP1_CRC, 0x00},
+ { CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL, 0x38},
+};
+
+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_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_TOP_FS_UNGATE:
+ case CDC_WSA_TOP_GRP_SEL:
+ case CDC_WSA_TOP_FS_UNGATE2:
+ case CDC_2_5_WSA_COMPANDER0_CTL8:
+ case CDC_2_5_WSA_COMPANDER0_CTL9:
+ case CDC_2_5_WSA_COMPANDER0_CTL10:
+ case CDC_2_5_WSA_COMPANDER0_CTL11:
+ case CDC_2_5_WSA_COMPANDER0_CTL12:
+ case CDC_2_5_WSA_COMPANDER0_CTL13:
+ case CDC_2_5_WSA_COMPANDER0_CTL14:
+ case CDC_2_5_WSA_COMPANDER0_CTL15:
+ case CDC_2_5_WSA_COMPANDER0_CTL16:
+ case CDC_2_5_WSA_COMPANDER0_CTL17:
+ case CDC_2_5_WSA_COMPANDER0_CTL18:
+ case CDC_2_5_WSA_COMPANDER0_CTL19:
+ case CDC_2_5_WSA_COMPANDER1_CTL0:
+ case CDC_2_5_WSA_COMPANDER1_CTL1:
+ case CDC_2_5_WSA_COMPANDER1_CTL2:
+ case CDC_2_5_WSA_COMPANDER1_CTL3:
+ case CDC_2_5_WSA_COMPANDER1_CTL4:
+ case CDC_2_5_WSA_COMPANDER1_CTL5:
+ case CDC_2_5_WSA_COMPANDER1_CTL7:
+ case CDC_2_5_WSA_COMPANDER1_CTL8:
+ case CDC_2_5_WSA_COMPANDER1_CTL9:
+ case CDC_2_5_WSA_COMPANDER1_CTL10:
+ case CDC_2_5_WSA_COMPANDER1_CTL11:
+ case CDC_2_5_WSA_COMPANDER1_CTL12:
+ case CDC_2_5_WSA_COMPANDER1_CTL13:
+ case CDC_2_5_WSA_COMPANDER1_CTL14:
+ case CDC_2_5_WSA_COMPANDER1_CTL15:
+ case CDC_2_5_WSA_COMPANDER1_CTL16:
+ case CDC_2_5_WSA_COMPANDER1_CTL17:
+ case CDC_2_5_WSA_COMPANDER1_CTL18:
+ case CDC_2_5_WSA_COMPANDER1_CTL19:
+ case CDC_2_5_WSA_SOFTCLIP0_CRC:
+ case CDC_2_5_WSA_SOFTCLIP0_SOFTCLIP_CTRL:
+ case CDC_2_5_WSA_SOFTCLIP1_CRC:
+ case CDC_2_5_WSA_SOFTCLIP1_SOFTCLIP_CTRL:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_rw_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_rw_register_v2_5(dev, reg);
+
+ return wsa_is_rw_register_v2_1(dev, reg);
+}
+
+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_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return wsa_is_rw_register(dev, reg);
+}
+
+static bool wsa_is_readable_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_readable_register_v2_5(dev, reg);
+
+ return wsa_is_readable_register_v2_1(dev, reg);
+}
+
+static bool wsa_is_volatile_register_v2_1(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register_v2_5(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CDC_2_5_WSA_COMPANDER1_CTL6:
+ return true;
+ }
+
+ return false;
+}
+
+static bool wsa_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ /* 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_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;
+ }
+
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5)
+ return wsa_is_volatile_register_v2_5(dev, reg);
+
+ return wsa_is_volatile_register_v2_1(dev, reg);
+}
+
+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 and .num_reg_defaults set in probe() */
+ .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,
+ wsa->reg_layout->rx_intx_1_mix_inp0_sel_mask);
+ inp1_sel = snd_soc_component_read_field(component, int_mux_cfg0,
+ wsa->reg_layout->rx_intx_1_mix_inp1_sel_mask);
+ inp2_sel = snd_soc_component_read_field(component, int_mux_cfg1,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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;
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(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;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ if (dai->id == WSA_MACRO_AIF_VI)
+ wsa->pcm_rate_vi = params_rate(params);
+
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int wsa_macro_get_channel_map(const 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 void wsa_macro_enable_disable_vi_sense(struct snd_soc_component *component, bool enable,
+ u32 tx_reg0, u32 tx_reg1, u32 val)
+{
+ if (enable) {
+ /* 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,
+ val);
+ snd_soc_component_update_bits(component, tx_reg1,
+ CDC_WSA_TX_SPKR_PROT_PCM_RATE_MASK,
+ val);
+ 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);
+ } else {
+ 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);
+ }
+}
+
+static void wsa_macro_enable_disable_vi_feedback(struct snd_soc_component *component,
+ bool enable, u32 rate)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+
+ if (test_bit(WSA_MACRO_TX0, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX0_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX1_SPKR_PROT_PATH_CTL, rate);
+
+ if (test_bit(WSA_MACRO_TX1, &wsa->active_ch_mask[WSA_MACRO_AIF_VI]))
+ wsa_macro_enable_disable_vi_sense(component, enable,
+ CDC_WSA_TX2_SPKR_PROT_PATH_CTL,
+ CDC_WSA_TX3_SPKR_PROT_PATH_CTL, rate);
+}
+
+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 rate_val;
+
+ switch (wsa->pcm_rate_vi) {
+ case 8000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ case 16000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_16K;
+ break;
+ case 24000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_24K;
+ break;
+ case 32000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_32K;
+ break;
+ case 48000:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_48K;
+ break;
+ default:
+ rate_val = CDC_WSA_TX_SPKR_PROT_PCM_RATE_8K;
+ break;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Enable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, true, rate_val);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Disable V&I sensing */
+ wsa_macro_enable_disable_vi_feedback(component, false, rate_val);
+ 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->reg_layout->compander1_reg_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 = wsa->reg_layout->softclip0_reg_base +
+ (path * wsa->reg_layout->softclip1_reg_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->reg_layout->softclip1_reg_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)
+{
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ 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,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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,
+ wsa->reg_layout->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;
+ 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);
+ }
+ val = snd_soc_component_read(component, gain_reg);
+ 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);
+ }
+ 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;
+ unsigned int dai_id;
+ 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:
+ /*
+ * active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS).
+ * active_ch_cnt == 0 was tested in if() above.
+ */
+ dai_id = aif_rst - 1;
+ if (wsa->active_ch_cnt[dai_id]) {
+ clear_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ break;
+ case 1:
+ case 2:
+ /* active_ch_cnt and active_ch_mask use DAI IDs (WSA_MACRO_MAX_DAIS). */
+ dai_id = rx_port_value - 1;
+ set_bit(bit_input, &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ 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;
+ u32 dai_id = widget->shift;
+
+ if (enable) {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ !test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ !test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ set_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]++;
+ }
+ } else {
+ if (spk_tx_id == WSA_MACRO_TX0 &&
+ test_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX0,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ if (spk_tx_id == WSA_MACRO_TX1 &&
+ test_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id])) {
+ clear_bit(WSA_MACRO_TX1,
+ &wsa->active_ch_mask[dai_id]);
+ wsa->active_ch_cnt[dai_id]--;
+ }
+ }
+ 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_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_widget wsa_macro_dapm_widgets_v2_1[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0,
+ 0, &rx0_mix_mux_v2_1, 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_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_1),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_1),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1,
+ 0, &rx1_mix_mux_v2_1, wsa_macro_enable_mix_path,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_widget wsa_macro_dapm_widgets_v2_5[] = {
+ SND_SOC_DAPM_MUX("WSA_RX0 INP0", SND_SOC_NOPM, 0, 0, &rx0_prim_inp0_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP1", SND_SOC_NOPM, 0, 0, &rx0_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX0 INP2", SND_SOC_NOPM, 0, 0, &rx0_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX_E("WSA_RX0 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX0,
+ 0, &rx0_mix_mux_v2_5, 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_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP1", SND_SOC_NOPM, 0, 0, &rx1_prim_inp1_mux_v2_5),
+ SND_SOC_DAPM_MUX("WSA_RX1 INP2", SND_SOC_NOPM, 0, 0, &rx1_prim_inp2_mux_v2_5),
+ SND_SOC_DAPM_MUX_E("WSA_RX1 MIX INP", SND_SOC_NOPM, WSA_MACRO_RX_MIX1,
+ 0, &rx1_mix_mux_v2_5, wsa_macro_enable_mix_path,
+ 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 snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(comp);
+ struct wsa_macro *wsa = snd_soc_component_get_drvdata(comp);
+ const struct snd_soc_dapm_widget *widgets;
+ unsigned int num_widgets;
+
+ 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);
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ widgets = wsa_macro_dapm_widgets_v2_1;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_1);
+ break;
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ widgets = wsa_macro_dapm_widgets_v2_5;
+ num_widgets = ARRAY_SIZE(wsa_macro_dapm_widgets_v2_5);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
+}
+
+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, def_count;
+
+ 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->codec_version = lpass_macro_get_codec_version();
+ struct reg_default *reg_defaults __free(kfree) = NULL;
+
+ switch (wsa->codec_version) {
+ case LPASS_CODEC_VERSION_1_0:
+ case LPASS_CODEC_VERSION_1_1:
+ case LPASS_CODEC_VERSION_1_2:
+ case LPASS_CODEC_VERSION_2_0:
+ case LPASS_CODEC_VERSION_2_1:
+ wsa->reg_layout = &wsa_codec_v2_1;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_1);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_1, sizeof(wsa_defaults_v2_1));
+ break;
+
+ case LPASS_CODEC_VERSION_2_5:
+ case LPASS_CODEC_VERSION_2_6:
+ case LPASS_CODEC_VERSION_2_7:
+ case LPASS_CODEC_VERSION_2_8:
+ case LPASS_CODEC_VERSION_2_9:
+ wsa->reg_layout = &wsa_codec_v2_5;
+ def_count = ARRAY_SIZE(wsa_defaults) + ARRAY_SIZE(wsa_defaults_v2_5);
+ reg_defaults = kmalloc_array(def_count, sizeof(*reg_defaults),
+ GFP_KERNEL);
+ if (!reg_defaults)
+ return -ENOMEM;
+ memcpy(&reg_defaults[0], wsa_defaults, sizeof(wsa_defaults));
+ memcpy(&reg_defaults[ARRAY_SIZE(wsa_defaults)],
+ wsa_defaults_v2_5, sizeof(wsa_defaults_v2_5));
+ break;
+
+ default:
+ dev_err(dev, "Unsupported Codec version (%d)\n", wsa->codec_version);
+ return -EINVAL;
+ }
+
+ struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config,
+ sizeof(*reg_config),
+ GFP_KERNEL);
+ if (!reg_config)
+ return -ENOMEM;
+
+ reg_config->reg_defaults = reg_defaults;
+ reg_config->num_reg_defaults = def_count;
+
+ wsa->regmap = devm_regmap_init_mmio(dev, base, reg_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 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 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 = {
+ 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 = pm_ptr(&wsa_macro_pm_ops),
+ },
+ .probe = wsa_macro_probe,
+ .remove = 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 000000000000..d3d62b3f6500
--- /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 000000000000..bc3470cf2c54
--- /dev/null
+++ b/sound/soc/codecs/madera.c
@@ -0,0 +1,4812 @@
+// 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 <linux/delay.h>
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/string_choices.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <linux/irqchip/irq-madera.h>
+#include <linux/mfd/madera/core.h>
+#include <linux/mfd/madera/registers.h>
+#include <linux/mfd/madera/pdata.h>
+#include <sound/madera-pdata.h>
+
+#include <dt-bindings/sound/madera.h>
+
+#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 = 6000;
+ break;
+ default:
+ out_up_delay = 17000;
+ 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) {
+ fsleep(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 += 1000;
+ 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) {
+ fsleep(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_CBP_CFP) {
+ 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_CBP_CFP) {
+ 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_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ lrclk |= MADERA_AIF1TX_LRCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ bclk |= MADERA_AIF1_BCLK_MSTR;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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, &reg);
+ 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, &reg_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_SRC_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",
+ str_enabled_disabled(already_enabled));
+
+ 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",
+ str_enabled_disabled(already_enabled));
+
+ /* 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",
+ str_enabled_disabled(already_enabled));
+
+ /* 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 <ckeepax@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/madera.h b/sound/soc/codecs/madera.h
new file mode 100644
index 000000000000..09ad6e9bce4b
--- /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 <linux/completion.h>
+#include <sound/soc.h>
+#include <sound/madera-pdata.h>
+
+#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 000000000000..bc57d7687f16
--- /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 <narmstrong@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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 <narmstrong@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index e1c196a41930..8af3c7e5317f 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -1,18 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX9768 AMP driver
*
* Copyright (C) 2011, 2012 by Wolfram Sang, Pengutronix e.K.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; version 2 of the License.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -30,12 +27,12 @@
struct max9768 {
struct regmap *regmap;
- int mute_gpio;
- int shdn_gpio;
+ struct gpio_desc *mute;
+ struct gpio_desc *shdn;
u32 flags;
};
-static struct reg_default max9768_default_regs[] = {
+static const struct reg_default max9768_default_regs[] = {
{ 0, 0 },
{ 3, MAX9768_CTRL_FILTERLESS},
};
@@ -43,9 +40,9 @@ static struct reg_default max9768_default_regs[] = {
static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
- int val = gpio_get_value_cansleep(max9768->mute_gpio);
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+ int val = gpiod_get_value_cansleep(max9768->mute);
ucontrol->value.integer.value[0] = !val;
@@ -55,16 +52,22 @@ static int max9768_get_gpio(struct snd_kcontrol *kcontrol,
static int max9768_set_gpio(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(c);
+ bool val = !ucontrol->value.integer.value[0];
+ int ret;
- gpio_set_value_cansleep(max9768->mute_gpio, !ucontrol->value.integer.value[0]);
+ if (val != gpiod_get_value_cansleep(max9768->mute))
+ ret = 1;
+ else
+ ret = 0;
- return 0;
+ gpiod_set_value_cansleep(max9768->mute, val);
+
+ return ret;
}
-static const unsigned int volume_tlv[] = {
- TLV_DB_RANGE_HEAD(43),
+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),
@@ -107,8 +110,8 @@ static const unsigned int volume_tlv[] = {
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),
-};
+ 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),
@@ -130,19 +133,20 @@ static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
{ "OUT-", NULL, "IN" },
};
-static int max9768_probe(struct snd_soc_codec *codec)
+static int max9768_probe(struct snd_soc_component *component)
{
- struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
+ struct max9768 *max9768 = snd_soc_component_get_drvdata(component);
int ret;
if (max9768->flags & MAX9768_FLAG_CLASSIC_PWM) {
- ret = snd_soc_write(codec, MAX9768_CTRL, MAX9768_CTRL_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_codec_controls(codec, max9768_mute,
+ if (max9768->mute) {
+ ret = snd_soc_add_component_controls(component, max9768_mute,
ARRAY_SIZE(max9768_mute));
if (ret)
return ret;
@@ -151,7 +155,7 @@ static int max9768_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver max9768_codec_driver = {
+static const struct snd_soc_component_driver max9768_component_driver = {
.probe = max9768_probe,
.controls = max9768_volume,
.num_controls = ARRAY_SIZE(max9768_volume),
@@ -170,71 +174,46 @@ static const struct regmap_config max9768_i2c_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9768_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+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 = gpio_request_one(pdata->mute_gpio, GPIOF_INIT_HIGH, "MAX9768 Mute");
- max9768->mute_gpio = err ?: pdata->mute_gpio;
-
- /* Activate chip by releasing shutdown, enables I2C */
- err = gpio_request_one(pdata->shdn_gpio, GPIOF_INIT_HIGH, "MAX9768 Shutdown");
- max9768->shdn_gpio = err ?: pdata->shdn_gpio;
-
+ /* Mute on powerup to avoid clicks */
+ max9768->mute = devm_gpiod_get_optional(&client->dev,
+ "mute",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->mute))
+ return PTR_ERR(max9768->mute);
+ gpiod_set_consumer_name(max9768->mute, "MAX9768 Mute");
+
+ /* Activate chip by releasing shutdown, enables I2C */
+ max9768->shdn = devm_gpiod_get_optional(&client->dev,
+ "shutdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max9768->shdn))
+ return PTR_ERR(max9768->shdn);
+ gpiod_set_consumer_name(max9768->shdn, "MAX9768 Shutdown");
+
+ if (pdata)
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)) {
- err = PTR_ERR(max9768->regmap);
- goto err_gpio_free;
- }
-
- err = snd_soc_register_codec(&client->dev, &max9768_codec_driver, NULL, 0);
- if (err)
- goto err_gpio_free;
+ if (IS_ERR(max9768->regmap))
+ return PTR_ERR(max9768->regmap);
- return 0;
-
- err_gpio_free:
- if (gpio_is_valid(max9768->shdn_gpio))
- gpio_free(max9768->shdn_gpio);
- if (gpio_is_valid(max9768->mute_gpio))
- gpio_free(max9768->mute_gpio);
-
- return err;
-}
-
-static int max9768_i2c_remove(struct i2c_client *client)
-{
- struct max9768 *max9768 = i2c_get_clientdata(client);
-
- snd_soc_unregister_codec(&client->dev);
-
- if (gpio_is_valid(max9768->shdn_gpio))
- gpio_free(max9768->shdn_gpio);
- if (gpio_is_valid(max9768->mute_gpio))
- gpio_free(max9768->mute_gpio);
-
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &max9768_component_driver, NULL, 0);
}
static const struct i2c_device_id max9768_i2c_id[] = {
- { "max9768", 0 },
+ { "max9768" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
@@ -242,14 +221,12 @@ MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
static struct i2c_driver max9768_i2c_driver = {
.driver = {
.name = "max9768",
- .owner = THIS_MODULE,
},
.probe = max9768_i2c_probe,
- .remove = max9768_i2c_remove,
.id_table = max9768_i2c_id,
};
module_i2c_driver(max9768_i2c_driver);
-MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
+MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
MODULE_DESCRIPTION("ASoC MAX9768 amplifier driver");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 805b3f8cd39d..37e61d8d4be6 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98088.c -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * 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.
*/
#include <linux/module.h>
@@ -16,6 +13,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/clk.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -42,6 +40,8 @@ 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;
@@ -258,292 +258,36 @@ static const struct reg_default max98088_reg[] = {
{ 0xc9, 0x00 }, /* C9 DAI2 biquad */
};
-static struct {
- int readable;
- int writable;
- int vol;
-} max98088_access[M98088_REG_CNT] = {
- { 0xFF, 0xFF, 1 }, /* 00 IRQ status */
- { 0xFF, 0x00, 1 }, /* 01 MIC status */
- { 0xFF, 0x00, 1 }, /* 02 jack status */
- { 0x1F, 0x1F, 1 }, /* 03 battery voltage */
- { 0xFF, 0xFF, 0 }, /* 04 */
- { 0xFF, 0xFF, 0 }, /* 05 */
- { 0xFF, 0xFF, 0 }, /* 06 */
- { 0xFF, 0xFF, 0 }, /* 07 */
- { 0xFF, 0xFF, 0 }, /* 08 */
- { 0xFF, 0xFF, 0 }, /* 09 */
- { 0xFF, 0xFF, 0 }, /* 0A */
- { 0xFF, 0xFF, 0 }, /* 0B */
- { 0xFF, 0xFF, 0 }, /* 0C */
- { 0xFF, 0xFF, 0 }, /* 0D */
- { 0xFF, 0xFF, 0 }, /* 0E */
- { 0xFF, 0xFF, 0 }, /* 0F interrupt enable */
-
- { 0xFF, 0xFF, 0 }, /* 10 master clock */
- { 0xFF, 0xFF, 0 }, /* 11 DAI1 clock mode */
- { 0xFF, 0xFF, 0 }, /* 12 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 13 DAI1 clock control */
- { 0xFF, 0xFF, 0 }, /* 14 DAI1 format */
- { 0xFF, 0xFF, 0 }, /* 15 DAI1 clock */
- { 0xFF, 0xFF, 0 }, /* 16 DAI1 config */
- { 0xFF, 0xFF, 0 }, /* 17 DAI1 TDM */
- { 0xFF, 0xFF, 0 }, /* 18 DAI1 filters */
- { 0xFF, 0xFF, 0 }, /* 19 DAI2 clock mode */
- { 0xFF, 0xFF, 0 }, /* 1A DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1B DAI2 clock control */
- { 0xFF, 0xFF, 0 }, /* 1C DAI2 format */
- { 0xFF, 0xFF, 0 }, /* 1D DAI2 clock */
- { 0xFF, 0xFF, 0 }, /* 1E DAI2 config */
- { 0xFF, 0xFF, 0 }, /* 1F DAI2 TDM */
-
- { 0xFF, 0xFF, 0 }, /* 20 DAI2 filters */
- { 0xFF, 0xFF, 0 }, /* 21 data config */
- { 0xFF, 0xFF, 0 }, /* 22 DAC mixer */
- { 0xFF, 0xFF, 0 }, /* 23 left ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 24 right ADC mixer */
- { 0xFF, 0xFF, 0 }, /* 25 left HP mixer */
- { 0xFF, 0xFF, 0 }, /* 26 right HP mixer */
- { 0xFF, 0xFF, 0 }, /* 27 HP control */
- { 0xFF, 0xFF, 0 }, /* 28 left REC mixer */
- { 0xFF, 0xFF, 0 }, /* 29 right REC mixer */
- { 0xFF, 0xFF, 0 }, /* 2A REC control */
- { 0xFF, 0xFF, 0 }, /* 2B left SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2C right SPK mixer */
- { 0xFF, 0xFF, 0 }, /* 2D SPK control */
- { 0xFF, 0xFF, 0 }, /* 2E sidetone */
- { 0xFF, 0xFF, 0 }, /* 2F DAI1 playback level */
-
- { 0xFF, 0xFF, 0 }, /* 30 DAI1 playback level */
- { 0xFF, 0xFF, 0 }, /* 31 DAI2 playback level */
- { 0xFF, 0xFF, 0 }, /* 32 DAI2 playbakc level */
- { 0xFF, 0xFF, 0 }, /* 33 left ADC level */
- { 0xFF, 0xFF, 0 }, /* 34 right ADC level */
- { 0xFF, 0xFF, 0 }, /* 35 MIC1 level */
- { 0xFF, 0xFF, 0 }, /* 36 MIC2 level */
- { 0xFF, 0xFF, 0 }, /* 37 INA level */
- { 0xFF, 0xFF, 0 }, /* 38 INB level */
- { 0xFF, 0xFF, 0 }, /* 39 left HP volume */
- { 0xFF, 0xFF, 0 }, /* 3A right HP volume */
- { 0xFF, 0xFF, 0 }, /* 3B left REC volume */
- { 0xFF, 0xFF, 0 }, /* 3C right REC volume */
- { 0xFF, 0xFF, 0 }, /* 3D left SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3E right SPK volume */
- { 0xFF, 0xFF, 0 }, /* 3F MIC config */
-
- { 0xFF, 0xFF, 0 }, /* 40 MIC threshold */
- { 0xFF, 0xFF, 0 }, /* 41 excursion limiter filter */
- { 0xFF, 0xFF, 0 }, /* 42 excursion limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 43 ALC */
- { 0xFF, 0xFF, 0 }, /* 44 power limiter threshold */
- { 0xFF, 0xFF, 0 }, /* 45 power limiter config */
- { 0xFF, 0xFF, 0 }, /* 46 distortion limiter config */
- { 0xFF, 0xFF, 0 }, /* 47 audio input */
- { 0xFF, 0xFF, 0 }, /* 48 microphone */
- { 0xFF, 0xFF, 0 }, /* 49 level control */
- { 0xFF, 0xFF, 0 }, /* 4A bypass switches */
- { 0xFF, 0xFF, 0 }, /* 4B jack detect */
- { 0xFF, 0xFF, 0 }, /* 4C input enable */
- { 0xFF, 0xFF, 0 }, /* 4D output enable */
- { 0xFF, 0xFF, 0 }, /* 4E bias control */
- { 0xFF, 0xFF, 0 }, /* 4F DAC power */
-
- { 0xFF, 0xFF, 0 }, /* 50 DAC power */
- { 0xFF, 0xFF, 0 }, /* 51 system */
- { 0xFF, 0xFF, 0 }, /* 52 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 53 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 54 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 55 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 56 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 57 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 58 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 59 DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5A DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5B DAI1 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 5C DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5D DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5E DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 5F DAI1 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 60 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 61 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 62 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 63 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 64 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 65 DAI1 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 66 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 67 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 68 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 69 DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6A DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6B DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6C DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6D DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6E DAI1 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 6F DAI1 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* 70 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 71 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 72 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 73 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 74 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 75 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 76 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 77 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 78 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 79 DAI1 EQ4 */
- { 0xFF, 0xFF, 0 }, /* 7A DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7B DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7C DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7D DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7E DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 7F DAI1 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* 80 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 81 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 82 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 83 DAI1 EQ5 */
- { 0xFF, 0xFF, 0 }, /* 84 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 85 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 86 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 87 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 88 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 89 DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8A DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8B DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8C DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8D DAI2 EQ1 */
- { 0xFF, 0xFF, 0 }, /* 8E DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 8F DAI2 EQ2 */
-
- { 0xFF, 0xFF, 0 }, /* 90 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 91 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 92 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 93 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 94 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 95 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 96 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 97 DAI2 EQ2 */
- { 0xFF, 0xFF, 0 }, /* 98 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 99 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9A DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9B DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9C DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9D DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9E DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* 9F DAI2 EQ3 */
-
- { 0xFF, 0xFF, 0 }, /* A0 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A1 DAI2 EQ3 */
- { 0xFF, 0xFF, 0 }, /* A2 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A3 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A4 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A5 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A6 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A7 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A8 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* A9 DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AA DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AB DAI2 EQ4 */
- { 0xFF, 0xFF, 0 }, /* AC DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AD DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AE DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* AF DAI2 EQ5 */
-
- { 0xFF, 0xFF, 0 }, /* B0 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B1 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B2 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B3 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B4 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B5 DAI2 EQ5 */
- { 0xFF, 0xFF, 0 }, /* B6 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B7 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B8 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* B9 DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BA DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BB DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BC DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BD DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BE DAI1 biquad */
- { 0xFF, 0xFF, 0 }, /* BF DAI1 biquad */
-
- { 0xFF, 0xFF, 0 }, /* C0 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C1 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C2 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C3 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C4 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C5 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C6 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C7 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C8 DAI2 biquad */
- { 0xFF, 0xFF, 0 }, /* C9 DAI2 biquad */
- { 0x00, 0x00, 0 }, /* CA */
- { 0x00, 0x00, 0 }, /* CB */
- { 0x00, 0x00, 0 }, /* CC */
- { 0x00, 0x00, 0 }, /* CD */
- { 0x00, 0x00, 0 }, /* CE */
- { 0x00, 0x00, 0 }, /* CF */
-
- { 0x00, 0x00, 0 }, /* D0 */
- { 0x00, 0x00, 0 }, /* D1 */
- { 0x00, 0x00, 0 }, /* D2 */
- { 0x00, 0x00, 0 }, /* D3 */
- { 0x00, 0x00, 0 }, /* D4 */
- { 0x00, 0x00, 0 }, /* D5 */
- { 0x00, 0x00, 0 }, /* D6 */
- { 0x00, 0x00, 0 }, /* D7 */
- { 0x00, 0x00, 0 }, /* D8 */
- { 0x00, 0x00, 0 }, /* D9 */
- { 0x00, 0x00, 0 }, /* DA */
- { 0x00, 0x00, 0 }, /* DB */
- { 0x00, 0x00, 0 }, /* DC */
- { 0x00, 0x00, 0 }, /* DD */
- { 0x00, 0x00, 0 }, /* DE */
- { 0x00, 0x00, 0 }, /* DF */
-
- { 0x00, 0x00, 0 }, /* E0 */
- { 0x00, 0x00, 0 }, /* E1 */
- { 0x00, 0x00, 0 }, /* E2 */
- { 0x00, 0x00, 0 }, /* E3 */
- { 0x00, 0x00, 0 }, /* E4 */
- { 0x00, 0x00, 0 }, /* E5 */
- { 0x00, 0x00, 0 }, /* E6 */
- { 0x00, 0x00, 0 }, /* E7 */
- { 0x00, 0x00, 0 }, /* E8 */
- { 0x00, 0x00, 0 }, /* E9 */
- { 0x00, 0x00, 0 }, /* EA */
- { 0x00, 0x00, 0 }, /* EB */
- { 0x00, 0x00, 0 }, /* EC */
- { 0x00, 0x00, 0 }, /* ED */
- { 0x00, 0x00, 0 }, /* EE */
- { 0x00, 0x00, 0 }, /* EF */
-
- { 0x00, 0x00, 0 }, /* F0 */
- { 0x00, 0x00, 0 }, /* F1 */
- { 0x00, 0x00, 0 }, /* F2 */
- { 0x00, 0x00, 0 }, /* F3 */
- { 0x00, 0x00, 0 }, /* F4 */
- { 0x00, 0x00, 0 }, /* F5 */
- { 0x00, 0x00, 0 }, /* F6 */
- { 0x00, 0x00, 0 }, /* F7 */
- { 0x00, 0x00, 0 }, /* F8 */
- { 0x00, 0x00, 0 }, /* F9 */
- { 0x00, 0x00, 0 }, /* FA */
- { 0x00, 0x00, 0 }, /* FB */
- { 0x00, 0x00, 0 }, /* FC */
- { 0x00, 0x00, 0 }, /* FD */
- { 0x00, 0x00, 0 }, /* FE */
- { 0xFF, 0x00, 1 }, /* FF */
-};
-
static bool max98088_readable_register(struct device *dev, unsigned int reg)
{
- return max98088_access[reg].readable;
+ 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)
{
- return max98088_access[reg].vol;
+ 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 = {
@@ -551,6 +295,7 @@ static const struct regmap_config max98088_regmap = {
.val_bits = 8,
.readable_reg = max98088_readable_register,
+ .writeable_reg = max98088_writeable_register,
.volatile_reg = max98088_volatile_register,
.max_register = 0xff,
@@ -562,27 +307,27 @@ static const struct regmap_config max98088_regmap = {
/*
* Load equalizer DSP coefficient configurations registers
*/
-static void m98088_eq_band(struct snd_soc_codec *codec, unsigned int dai,
+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;
+ 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;
+ /* 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);
+ /* 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_write(codec, eq_reg++, M98088_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98088_BYTE0(coefs[i]));
- }
+ /* 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]));
+ }
}
/*
@@ -635,12 +380,12 @@ static SOC_ENUM_SINGLE_DECL(max98088_dai1_adc_filter_enum,
static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_35_LVL_MIC1, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -649,8 +394,8 @@ static int max98088_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic1pre;
return 0;
@@ -659,12 +404,12 @@ static int max98088_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98088_REG_36_LVL_MIC2, M98088_MICPRE_MASK,
(1+sel)<<M98088_MICPRE_SHIFT);
return 0;
@@ -673,36 +418,33 @@ static int max98088_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98088_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98088->mic2pre;
return 0;
}
-static const unsigned int max98088_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
- 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_micboost_tlv,
+ 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0)
+);
-static const unsigned int max98088_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98088_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
static const struct snd_kcontrol_new max98088_snd_controls[] = {
@@ -732,15 +474,24 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
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("DACL Volume", M98088_REG_2F_LVL_DAI1_PLAY, 0, 15, 1),
+ SOC_SINGLE("DACR Volume", M98088_REG_31_LVL_DAI2_PLAY, 0, 15, 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("Left HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 4, 1, 0),
+ SOC_SINGLE("Right HP Output Mixer Switch", M98088_REG_27_MIX_HP_CNTL, 5, 1, 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),
@@ -770,10 +521,8 @@ static const struct snd_kcontrol_new max98088_snd_controls[] = {
/* 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("Left DAC Switch", M98088_REG_2B_MIX_SPK_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -784,10 +533,8 @@ static const struct snd_kcontrol_new max98088_left_speaker_mixer_controls[] = {
/* 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("Left DAC Switch", M98088_REG_2C_MIX_SPK_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -798,10 +545,8 @@ static const struct snd_kcontrol_new max98088_right_speaker_mixer_controls[] = {
/* 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("Left DAC Switch", M98088_REG_25_MIX_HP_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -812,10 +557,8 @@ static const struct snd_kcontrol_new max98088_left_hp_mixer_controls[] = {
/* 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("Left DAC Switch", M98088_REG_26_MIX_HP_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -826,10 +569,8 @@ static const struct snd_kcontrol_new max98088_right_hp_mixer_controls[] = {
/* 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("Left DAC Switch", M98088_REG_28_MIX_REC_LEFT, 0, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -840,10 +581,8 @@ static const struct snd_kcontrol_new max98088_left_rec_mixer_controls[] = {
/* 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("Left DAC Switch", M98088_REG_29_MIX_REC_RIGHT, 7, 1, 0),
+ SOC_DAPM_SINGLE("Right DAC 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),
@@ -875,21 +614,21 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
static int max98088_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic1pre)<<M98088_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK,
(1+max98088->mic2pre)<<M98088_MICPRE_SHIFT);
}
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, M98088_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98088_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -905,8 +644,8 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
static int max98088_line_pga(struct snd_soc_dapm_widget *w,
int event, int line, u8 channel)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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)))
@@ -926,13 +665,13 @@ static int max98088_line_pga(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
*state |= channel;
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), 0);
}
break;
@@ -972,13 +711,9 @@ 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",
+ SND_SOC_DAPM_DAC("DACL", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 1, 0),
- SND_SOC_DAPM_DAC("DACR2", "Aux Playback",
+ SND_SOC_DAPM_DAC("DACR", "HiFi Playback",
M98088_REG_4D_PWR_EN_OUT, 0, 0),
SND_SOC_DAPM_PGA("HP Left Out", M98088_REG_4D_PWR_EN_OUT,
@@ -1074,10 +809,8 @@ static const struct snd_soc_dapm_widget max98088_dapm_widgets[] = {
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", "Left DAC Switch", "DACL"},
+ {"Left HP Mixer", "Right DAC Switch", "DACR"},
{"Left HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -1086,10 +819,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"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", "Left DAC Switch", "DACL"},
+ {"Right HP Mixer", "Right DAC Switch", "DACR"},
{"Right HP Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right HP Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right HP Mixer", "INA1 Switch", "INA1 Input"},
@@ -1098,10 +829,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"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", "Left DAC Switch", "DACL"},
+ {"Left SPK Mixer", "Right DAC Switch", "DACR"},
{"Left SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -1110,10 +839,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"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", "Left DAC Switch", "DACL"},
+ {"Right SPK Mixer", "Right DAC Switch", "DACR"},
{"Right SPK Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right SPK Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right SPK Mixer", "INA1 Switch", "INA1 Input"},
@@ -1122,10 +849,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"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", "Left DAC Switch", "DACL"},
+ {"Left REC Mixer", "Right DAC Switch", "DACR"},
{"Left REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Left REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Left REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -1134,10 +859,8 @@ static const struct snd_soc_dapm_route max98088_audio_map[] = {
{"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", "Left DAC Switch", "DACL"},
+ {"Right REC Mixer", "Right DAC Switch", "DACR"},
{"Right REC Mixer", "MIC1 Switch", "MIC1 Input"},
{"Right REC Mixer", "MIC2 Switch", "MIC2 Input"},
{"Right REC Mixer", "INA1 Switch", "INA1 Input"},
@@ -1221,8 +944,8 @@ 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_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1234,51 +957,54 @@ static int max98088_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_14_DAI1_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_11_DAI1_CLKMODE,
+ 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_read(codec, M98088_REG_14_DAI1_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_14_DAI1_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ 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)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ 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_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_18_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_18_DAI1_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1288,8 +1014,8 @@ 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_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1301,51 +1027,54 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98088_REG_1C_DAI2_FORMAT,
M98088_DAI_WS, M98088_DAI_WS);
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN, 0);
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98088_REG_19_DAI2_CLKMODE,
+ 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_read(codec, M98088_REG_1C_DAI2_FORMAT)
+ if (snd_soc_component_read(component, M98088_REG_1C_DAI2_FORMAT)
& M98088_DAI_MAS) {
+ unsigned long pclk;
+
if (max98088->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ 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)max98088->sysclk);
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ 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_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98088_REG_20_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98088_REG_20_DAI2_FILTERS,
M98088_DAI_DHF, M98088_DAI_DHF);
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS, M98088_SHDNRUN,
M98088_SHDNRUN);
return 0;
@@ -1354,30 +1083,37 @@ static int max98088_dai2_hw_params(struct snd_pcm_substream *substream,
static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, M98088_REG_10_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x10);
+ max98088->mclk_prescaler = 1;
} else if ((freq >= 20000000) && (freq < 30000000)) {
- snd_soc_write(codec, M98088_REG_10_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98088_REG_10_SYS_CLK, 0x20);
+ max98088->mclk_prescaler = 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
- if (snd_soc_read(codec, M98088_REG_51_PWR_SYS) & M98088_SHDNRUN) {
- snd_soc_update_bits(codec, M98088_REG_51_PWR_SYS,
+ 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_update_bits(codec, M98088_REG_51_PWR_SYS,
+ snd_soc_component_update_bits(component, M98088_REG_51_PWR_SYS,
M98088_SHDNRUN, M98088_SHDNRUN);
}
@@ -1390,8 +1126,8 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1401,22 +1137,20 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_12_DAI1_CLKCFG_HI,
+ 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_write(codec, M98088_REG_13_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_13_DAI1_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg14val |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1446,14 +1180,14 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_14_DAI1_FORMAT,
+ 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_write(codec, M98088_REG_15_DAI1_CLOCK, reg15val);
+ snd_soc_component_write(component, M98088_REG_15_DAI1_CLOCK, reg15val);
}
return 0;
@@ -1462,8 +1196,8 @@ static int max98088_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1472,22 +1206,20 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98088_REG_1A_DAI2_CLKCFG_HI,
+ 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_write(codec, M98088_REG_1B_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98088_REG_1B_DAI2_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
reg1Cval |= M98088_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1517,20 +1249,21 @@ static int max98088_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98088_REG_1C_DAI2_FORMAT,
+ 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_write(codec, M98088_REG_1D_DAI2_CLOCK,
+ snd_soc_component_write(component, M98088_REG_1D_DAI2_CLOCK,
M98088_DAI_BSEL64);
}
return 0;
}
-static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai1_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg;
if (mute)
@@ -1538,14 +1271,15 @@ static int max98088_dai1_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_2F_LVL_DAI1_PLAY,
+ snd_soc_component_update_bits(component, M98088_REG_2F_LVL_DAI1_PLAY,
M98088_DAI_MUTE_MASK, reg);
return 0;
}
-static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98088_dai2_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int reg;
if (mute)
@@ -1553,38 +1287,55 @@ static int max98088_dai2_digital_mute(struct snd_soc_dai *codec_dai, int mute)
else
reg = 0;
- snd_soc_update_bits(codec, M98088_REG_31_LVL_DAI2_PLAY,
+ 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_codec *codec,
+static int max98088_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = 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(max98088->mclk)) {
+ if (snd_soc_component_get_bias_level(component) ==
+ SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(max98088->mclk);
+ } else {
+ ret = clk_prepare_enable(max98088->mclk);
+ if (ret)
+ return ret;
+ }
+ }
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(max98088->regmap);
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, M98088_MBEN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98088_REG_4C_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98088_REG_4C_PWR_EN_IN,
M98088_MBEN, 0);
regcache_mark_dirty(max98088->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1595,14 +1346,16 @@ 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,
- .digital_mute = max98088_dai1_digital_mute,
+ .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,
- .digital_mute = max98088_dai2_digital_mute,
+ .mute_stream = max98088_dai2_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver max98088_dai[] = {
@@ -1639,22 +1392,19 @@ static struct snd_soc_dai_driver max98088_dai[] = {
static const char *eq_mode_name[] = {"EQ1 Mode", "EQ2 Mode"};
-static int max98088_get_channel(struct snd_soc_codec *codec, const char *name)
+static int max98088_get_channel(struct snd_soc_component *component, const char *name)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(eq_mode_name); i++)
- if (strcmp(name, eq_mode_name[i]) == 0)
- return i;
+ int ret;
- /* Shouldn't happen */
- dev_err(codec->dev, "Bad EQ channel name '%s'\n", name);
- return -EINVAL;
+ 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_codec *codec)
+static void max98088_setup_eq1(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1679,29 +1429,29 @@ static void max98088_setup_eq1(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ 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_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, 0);
+ 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(codec, 0, 0, coef_set->band1);
- m98088_eq_band(codec, 0, 1, coef_set->band2);
- m98088_eq_band(codec, 0, 2, coef_set->band3);
- m98088_eq_band(codec, 0, 3, coef_set->band4);
- m98088_eq_band(codec, 0, 4, coef_set->band5);
+ 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_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
+ snd_soc_component_update_bits(component, M98088_REG_49_CFG_LEVEL, M98088_EQ1EN, save);
}
-static void max98088_setup_eq2(struct snd_soc_codec *codec)
+static void max98088_setup_eq2(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1726,36 +1476,36 @@ static void max98088_setup_eq2(struct snd_soc_codec *codec)
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ 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_read(codec, M98088_REG_49_CFG_LEVEL);
- snd_soc_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN, 0);
+ 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(codec, 1, 0, coef_set->band1);
- m98088_eq_band(codec, 1, 1, coef_set->band2);
- m98088_eq_band(codec, 1, 2, coef_set->band3);
- m98088_eq_band(codec, 1, 3, coef_set->band4);
- m98088_eq_band(codec, 1, 4, coef_set->band5);
+ 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_update_bits(codec, M98088_REG_49_CFG_LEVEL, M98088_EQ2EN,
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, kcontrol->id.name);
+ int channel = max98088_get_channel(component, kcontrol->id.name);
struct max98088_cdata *cdata;
- int sel = ucontrol->value.integer.value[0];
+ int sel = ucontrol->value.enumerated.item[0];
if (channel < 0)
return channel;
@@ -1769,10 +1519,10 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
switch (channel) {
case 0:
- max98088_setup_eq1(codec);
+ max98088_setup_eq1(component);
break;
case 1:
- max98088_setup_eq2(codec);
+ max98088_setup_eq2(component);
break;
}
@@ -1782,9 +1532,9 @@ static int max98088_put_eq_enum(struct snd_kcontrol *kcontrol,
static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- int channel = max98088_get_channel(codec, kcontrol->id.name);
+ 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)
@@ -1795,9 +1545,9 @@ static int max98088_get_eq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
struct max98088_eq_cfg *cfg;
unsigned int cfgcnt;
@@ -1850,19 +1600,19 @@ static void max98088_handle_eq_pdata(struct snd_soc_codec *codec)
max98088->eq_enum.texts = max98088->eq_texts;
max98088->eq_enum.items = max98088->eq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+ dev_err(component->dev, "Failed to add EQ control: %d\n", ret);
}
-static void max98088_handle_pdata(struct snd_soc_codec *codec)
+static void max98088_handle_pdata(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_pdata *pdata = max98088->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -1875,21 +1625,21 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
max98088->digmic = (regval ? 1 : 0);
- snd_soc_write(codec, M98088_REG_48_CFG_MIC, regval);
+ snd_soc_component_write(component, M98088_REG_48_CFG_MIC, regval);
/* Configure receiver output */
regval = ((pdata->receiver_mode) ? M98088_REC_LINEMODE : 0);
- snd_soc_update_bits(codec, M98088_REG_2A_MIC_REC_CNTL,
+ 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(codec);
+ max98088_handle_eq_pdata(component);
}
-static int max98088_probe(struct snd_soc_codec *codec)
+static int max98088_probe(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
struct max98088_cdata *cdata;
int ret = 0;
@@ -1917,105 +1667,109 @@ static int max98088_probe(struct snd_soc_codec *codec)
max98088->mic1pre = 0;
max98088->mic2pre = 0;
- ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
+ ret = snd_soc_component_read(component, M98088_REG_FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ dev_err(component->dev, "Failed to read device revision: %d\n",
ret);
goto err_access;
}
- dev_info(codec->dev, "revision %c\n", ret - 0x40 + 'A');
+ dev_info(component->dev, "revision %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
+ snd_soc_component_write(component, M98088_REG_51_PWR_SYS, M98088_PWRSV);
- snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
+ snd_soc_component_write(component, M98088_REG_0F_IRQ_ENABLE, 0x00);
- snd_soc_write(codec, M98088_REG_22_MIX_DAC,
+ 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_write(codec, M98088_REG_4E_BIAS_CNTL, 0xF0);
- snd_soc_write(codec, M98088_REG_50_DAC_BIAS2, 0x0F);
+ snd_soc_component_write(component, M98088_REG_4E_BIAS_CNTL, 0xF0);
+ snd_soc_component_write(component, M98088_REG_50_DAC_BIAS2, 0x0F);
- snd_soc_write(codec, M98088_REG_16_DAI1_IOCFG,
+ snd_soc_component_write(component, M98088_REG_16_DAI1_IOCFG,
M98088_S1NORMAL|M98088_SDATA);
- snd_soc_write(codec, M98088_REG_1E_DAI2_IOCFG,
+ snd_soc_component_write(component, M98088_REG_1E_DAI2_IOCFG,
M98088_S2NORMAL|M98088_SDATA);
- max98088_handle_pdata(codec);
+ max98088_handle_pdata(component);
err_access:
return ret;
}
-static int max98088_remove(struct snd_soc_codec *codec)
+static void max98088_remove(struct snd_soc_component *component)
{
- struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+ struct max98088_priv *max98088 = snd_soc_component_get_drvdata(component);
kfree(max98088->eq_texts);
-
- return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
- .probe = max98088_probe,
- .remove = max98088_remove,
- .set_bias_level = max98088_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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,
- const struct i2c_device_id *id)
+static int max98088_i2c_probe(struct i2c_client *i2c)
{
- struct max98088_priv *max98088;
- int ret;
+ struct max98088_priv *max98088;
- max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
- GFP_KERNEL);
- if (max98088 == NULL)
- return -ENOMEM;
+ 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->regmap = devm_regmap_init_i2c(i2c, &max98088_regmap);
+ if (IS_ERR(max98088->regmap))
+ return PTR_ERR(max98088->regmap);
- max98088->devtype = id->driver_data;
+ 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);
- i2c_set_clientdata(i2c, max98088);
- max98088->pdata = i2c->dev.platform_data;
+ max98088->devtype = (uintptr_t)i2c_get_match_data(i2c);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98088, &max98088_dai[0], 2);
- return ret;
-}
+ i2c_set_clientdata(i2c, max98088);
+ max98088->pdata = i2c->dev.platform_data;
-static int max98088_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_max98088,
+ &max98088_dai[0], 2);
}
-static const struct i2c_device_id max98088_i2c_id[] = {
- { "max98088", MAX98088 },
- { "max98089", MAX98089 },
- { }
+#if defined(CONFIG_OF)
+static const struct of_device_id max98088_of_match[] = {
+ { .compatible = "maxim,max98088" },
+ { .compatible = "maxim,max98089" },
+ { }
};
-MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
+MODULE_DEVICE_TABLE(of, max98088_of_match);
+#endif
static struct i2c_driver max98088_i2c_driver = {
.driver = {
.name = "max98088",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max98088_of_match),
},
- .probe = max98088_i2c_probe,
- .remove = max98088_i2c_remove,
+ .probe = max98088_i2c_probe,
.id_table = max98088_i2c_id,
};
diff --git a/sound/soc/codecs/max98088.h b/sound/soc/codecs/max98088.h
index be89a4f4aab8..4190e5ff38f9 100644
--- a/sound/soc/codecs/max98088.h
+++ b/sound/soc/codecs/max98088.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98088.h -- MAX98088 ALSA SoC Audio driver
*
* Copyright 2010 Maxim Integrated Products
- *
- * 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 _MAX98088_H
@@ -16,7 +13,7 @@
*/
#define M98088_REG_00_IRQ_STATUS 0x00
#define M98088_REG_01_MIC_STATUS 0x01
-#define M98088_REG_02_JACK_STAUS 0x02
+#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
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index b112b1c2c394..22177c1ce160 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98090.c -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 Maxim Integrated Products
- *
- * 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.
*/
#include <linux/delay.h>
@@ -13,7 +10,6 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm.h>
-#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/acpi.h>
@@ -27,7 +23,7 @@
#include "max98090.h"
/* Allows for sparsely populated register maps */
-static struct reg_default max98090_reg[] = {
+static const struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */
{ 0x03, 0x04 }, /* 03 Interrupt Masks */
{ 0x04, 0x00 }, /* 04 System Clock Quick */
@@ -267,75 +263,8 @@ static bool max98090_volatile_register(struct device *dev, unsigned int reg)
static bool max98090_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
- case M98090_REG_DEVICE_STATUS:
- case M98090_REG_JACK_STATUS:
- case M98090_REG_INTERRUPT_S:
- case M98090_REG_RESERVED:
- case M98090_REG_LINE_INPUT_CONFIG:
- case M98090_REG_LINE_INPUT_LEVEL:
- case M98090_REG_INPUT_MODE:
- case M98090_REG_MIC1_INPUT_LEVEL:
- case M98090_REG_MIC2_INPUT_LEVEL:
- case M98090_REG_MIC_BIAS_VOLTAGE:
- case M98090_REG_DIGITAL_MIC_ENABLE:
- case M98090_REG_DIGITAL_MIC_CONFIG:
- case M98090_REG_LEFT_ADC_MIXER:
- case M98090_REG_RIGHT_ADC_MIXER:
- case M98090_REG_LEFT_ADC_LEVEL:
- case M98090_REG_RIGHT_ADC_LEVEL:
- case M98090_REG_ADC_BIQUAD_LEVEL:
- case M98090_REG_ADC_SIDETONE:
- case M98090_REG_SYSTEM_CLOCK:
- case M98090_REG_CLOCK_MODE:
- case M98090_REG_CLOCK_RATIO_NI_MSB:
- case M98090_REG_CLOCK_RATIO_NI_LSB:
- case M98090_REG_CLOCK_RATIO_MI_MSB:
- case M98090_REG_CLOCK_RATIO_MI_LSB:
- case M98090_REG_MASTER_MODE:
- case M98090_REG_INTERFACE_FORMAT:
- case M98090_REG_TDM_CONTROL:
- case M98090_REG_TDM_FORMAT:
- case M98090_REG_IO_CONFIGURATION:
- case M98090_REG_FILTER_CONFIG:
- case M98090_REG_DAI_PLAYBACK_LEVEL:
- case M98090_REG_DAI_PLAYBACK_LEVEL_EQ:
- case M98090_REG_LEFT_HP_MIXER:
- case M98090_REG_RIGHT_HP_MIXER:
- case M98090_REG_HP_CONTROL:
- case M98090_REG_LEFT_HP_VOLUME:
- case M98090_REG_RIGHT_HP_VOLUME:
- case M98090_REG_LEFT_SPK_MIXER:
- case M98090_REG_RIGHT_SPK_MIXER:
- case M98090_REG_SPK_CONTROL:
- case M98090_REG_LEFT_SPK_VOLUME:
- case M98090_REG_RIGHT_SPK_VOLUME:
- case M98090_REG_DRC_TIMING:
- case M98090_REG_DRC_COMPRESSOR:
- case M98090_REG_DRC_EXPANDER:
- case M98090_REG_DRC_GAIN:
- case M98090_REG_RCV_LOUTL_MIXER:
- case M98090_REG_RCV_LOUTL_CONTROL:
- case M98090_REG_RCV_LOUTL_VOLUME:
- case M98090_REG_LOUTR_MIXER:
- case M98090_REG_LOUTR_CONTROL:
- case M98090_REG_LOUTR_VOLUME:
- case M98090_REG_JACK_DETECT:
- case M98090_REG_INPUT_ENABLE:
- case M98090_REG_OUTPUT_ENABLE:
- case M98090_REG_LEVEL_CONTROL:
- case M98090_REG_DSP_FILTER_ENABLE:
- case M98090_REG_BIAS_CONTROL:
- case M98090_REG_DAC_CONTROL:
- case M98090_REG_ADC_CONTROL:
- case M98090_REG_DEVICE_SHUTDOWN:
- case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68:
- case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E:
- case M98090_REG_DMIC3_VOLUME:
- case M98090_REG_DMIC4_VOLUME:
- case M98090_REG_DMIC34_BQ_PREATTEN:
- case M98090_REG_RECORD_TDM_SLOT:
- case M98090_REG_SAMPLE_RATE:
- case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E:
+ case M98090_REG_DEVICE_STATUS ... M98090_REG_INTERRUPT_S:
+ case M98090_REG_LINE_INPUT_CONFIG ... 0xD1:
case M98090_REG_REVISION_ID:
return true;
default:
@@ -351,7 +280,7 @@ static int max98090_reset(struct max98090_priv *max98090)
ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
M98090_SWRESET_MASK);
if (ret < 0) {
- dev_err(max98090->codec->dev,
+ dev_err(max98090->component->dev,
"Failed to reset codec: %d\n", ret);
return ret;
}
@@ -360,22 +289,20 @@ static int max98090_reset(struct max98090_priv *max98090)
return ret;
}
-static const unsigned int max98090_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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 unsigned int max98090_line_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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);
@@ -383,56 +310,49 @@ 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_sidetone_tlv, -6050, 200, 0);
-
-static const DECLARE_TLV_DB_SCALE(max98090_alc_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 unsigned int max98090_mixout_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0)
+);
-static const unsigned int max98090_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98090_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0)
+);
-static const unsigned int max98090_rcv_lout_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, mc->reg);
+ unsigned int val = snd_soc_component_read(component, mc->reg);
unsigned int *select;
switch (mc->reg) {
@@ -467,14 +387,16 @@ static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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 sel = ucontrol->value.integer.value[0];
- unsigned int val = snd_soc_read(codec, mc->reg);
+ 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:
@@ -492,6 +414,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
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 */
@@ -502,11 +429,11 @@ static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
sel = val;
}
- snd_soc_update_bits(codec, mc->reg,
+ snd_soc_component_update_bits(component, mc->reg,
mask << mc->shift,
sel << mc->shift);
- return 0;
+ return change;
}
static const char *max98090_perf_pwr_text[] =
@@ -806,10 +733,10 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, w->reg);
+ 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;
@@ -841,15 +768,28 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
}
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98090_MIC_PA1EN_MASK,
val << M98090_MIC_PA1EN_SHIFT);
else
- snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK,
+ 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,
@@ -877,18 +817,6 @@ 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);
-static const char *max98090_micpre_text[] = { "Off", "On" };
-
-static SOC_ENUM_SINGLE_DECL(max98090_pa1en_enum,
- M98090_REG_MIC1_INPUT_LEVEL,
- M98090_MIC_PA1EN_SHIFT,
- max98090_micpre_text);
-
-static SOC_ENUM_SINGLE_DECL(max98090_pa2en_enum,
- M98090_REG_MIC2_INPUT_LEVEL,
- M98090_MIC_PA2EN_SHIFT,
- max98090_micpre_text);
-
/* LINEA mixer switch */
static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG,
@@ -1158,9 +1086,11 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
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, NULL, 0),
+ 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, NULL, 0),
+ 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),
@@ -1205,10 +1135,12 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_adc_mixer_controls[0],
ARRAY_SIZE(max98090_right_adc_mixer_controls)),
- SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE,
- M98090_ADLEN_SHIFT, 0),
- SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE,
- M98090_ADREN_SHIFT, 0),
+ 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),
@@ -1265,14 +1197,14 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
&max98090_right_rcv_mixer_controls[0],
ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
- SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
- M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux),
+ SND_SOC_DAPM_MUX("LINMOD Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_linmod_mux),
- SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux),
+ SND_SOC_DAPM_MUX("MIXHPLSEL Mux", SND_SOC_NOPM, 0, 0,
+ &max98090_mixhplsel_mux),
- SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
- M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_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),
@@ -1497,16 +1429,16 @@ static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
{"DMIC4", NULL, "AHPF"},
};
-static int max98090_add_widgets(struct snd_soc_codec *codec)
+static int max98090_add_widgets(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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_codec_controls(codec, max98090_snd_controls,
+ snd_soc_add_component_controls(component, max98090_snd_controls,
ARRAY_SIZE(max98090_snd_controls));
if (max98090->devtype == MAX98091) {
- snd_soc_add_codec_controls(codec, max98091_snd_controls,
+ snd_soc_add_component_controls(component, max98091_snd_controls,
ARRAY_SIZE(max98091_snd_controls));
}
@@ -1553,24 +1485,24 @@ static const unsigned long long mi_value[] = {
8125, 1625, 1500, 25
};
-static void max98090_configure_bclk(struct snd_soc_codec *codec)
+static void max98090_configure_bclk(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
unsigned long long ni;
int i;
if (!max98090->sysclk) {
- dev_err(codec->dev, "No SYSCLK configured\n");
+ dev_err(component->dev, "No SYSCLK configured\n");
return;
}
if (!max98090->bclk || !max98090->lrclk) {
- dev_err(codec->dev, "No audio clocks configured\n");
+ dev_err(component->dev, "No audio clocks configured\n");
return;
}
/* Skip configuration when operating as slave */
- if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) &
+ if (!(snd_soc_component_read(component, M98090_REG_MASTER_MODE) &
M98090_MAS_MASK)) {
return;
}
@@ -1579,14 +1511,14 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
if ((pclk_rates[i] == max98090->sysclk) &&
(lrclk_rates[i] == max98090->lrclk)) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Found supported PCLK to LRCLK rates 0x%x\n",
i + 0x8);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK,
(i + 0x8) << M98090_FREQ_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
return;
}
@@ -1596,24 +1528,24 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
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(codec->dev,
+ dev_dbg(component->dev,
"Found user supported PCLK to LRCLK rates\n");
- dev_dbg(codec->dev, "i %d ni %lld mi %lld\n",
+ dev_dbg(component->dev, "i %d ni %lld mi %lld\n",
i, ni_value[i], mi_value[i]);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK,
1 << M98090_USE_M1_SHIFT);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_MSB,
(ni_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_NI_LSB,
ni_value[i] & 0xFF);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_MSB,
(mi_value[i] >> 8) & 0x7F);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB,
+ snd_soc_component_write(component, M98090_REG_CLOCK_RATIO_MI_LSB,
mi_value[i] & 0xFF);
return;
@@ -1623,9 +1555,9 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
/*
* Calculate based on MI = 65536 (not as good as either method above)
*/
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_FREQ_MASK, 0);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
/*
@@ -1636,20 +1568,20 @@ static void max98090_configure_bclk(struct snd_soc_codec *codec)
ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
* (unsigned long long int)max98090->lrclk;
do_div(ni, (unsigned long long int)max98090->sysclk);
- dev_info(codec->dev, "No better method found\n");
- dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni);
- snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+ 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_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
+ 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_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
struct max98090_cdata *cdata;
- u8 regval;
+ u8 regval, tdm_regval;
max98090->dai_fmt = fmt;
cdata = &max98090->dai[0];
@@ -1658,19 +1590,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
cdata->fmt = fmt;
regval = 0;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Set to slave mode PLL - MAS mode off */
- snd_soc_write(codec,
+ 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_write(codec,
+ snd_soc_component_write(component,
M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
- snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+ snd_soc_component_update_bits(component, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0);
max98090->master = false;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
if (max98090->tdm_slots == 4) {
/* TDM */
regval |= M98090_MAS_MASK |
@@ -1686,13 +1619,11 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
}
max98090->master = true;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(component->dev, "DAI clock mode unsupported");
return -EINVAL;
}
- snd_soc_write(codec, M98090_REG_MASTER_MODE, regval);
+ snd_soc_component_write(component, M98090_REG_MASTER_MODE, regval);
regval = 0;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -1705,9 +1636,10 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_RJ_MASK;
break;
case SND_SOC_DAIFMT_DSP_A:
- /* Not supported mode */
+ tdm_regval |= M98090_TDM_MASK;
+ break;
default:
- dev_err(codec->dev, "DAI format unsupported");
+ dev_err(component->dev, "DAI format unsupported");
return -EINVAL;
}
@@ -1724,7 +1656,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_BCI_MASK|M98090_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(component->dev, "DAI invert mode unsupported");
return -EINVAL;
}
@@ -1734,11 +1666,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
* seen for the case of TDM mode. The remaining cases have
* normal logic.
*/
- if (max98090->tdm_slots > 1)
+ if (tdm_regval)
regval ^= M98090_BCI_MASK;
- snd_soc_write(codec,
+ 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;
@@ -1747,43 +1688,32 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct max98090_cdata *cdata;
- cdata = &max98090->dai[0];
+ 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;
- max98090->tdm_slots = slots;
- max98090->tdm_width = slot_width;
-
- if (max98090->tdm_slots > 1) {
- /* SLOTL SLOTR SLOTDLY */
- snd_soc_write(codec, M98090_REG_TDM_FORMAT,
- 0 << M98090_TDM_SLOTL_SHIFT |
- 1 << M98090_TDM_SLOTR_SHIFT |
- 0 << M98090_TDM_SLOTDLY_SHIFT);
-
- /* FSW TDM */
- snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL,
- M98090_TDM_MASK,
- M98090_TDM_MASK);
- }
+ if (slot_width != 16)
+ return -EINVAL;
- /*
- * Normally advisable to set TDM first, but this permits either order
- */
- cdata->fmt = 0;
- max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+ 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_codec *codec,
+static int max98090_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1798,19 +1728,23 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (!IS_ERR(max98090->mclk)) {
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- clk_disable_unprepare(max98090->mclk);
- else
- clk_prepare_enable(max98090->mclk);
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98090->regmap);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -1819,12 +1753,11 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
/* Set internal pull-up to lowest power mode */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
regcache_mark_dirty(max98090->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1903,7 +1836,7 @@ static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
static int max98090_find_divisor(int target_freq, int pclk)
{
int current_diff = INT_MAX;
- int test_diff = INT_MAX;
+ int test_diff;
int divisor_index = 0;
int i;
@@ -1977,12 +1910,27 @@ static int max98090_configure_dmic(struct max98090_priv *max98090,
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_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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];
@@ -1994,7 +1942,7 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
+ snd_soc_component_update_bits(component, M98090_REG_INTERFACE_FORMAT,
M98090_WS_MASK, 0);
break;
default:
@@ -2002,24 +1950,24 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
}
if (max98090->master)
- max98090_configure_bclk(codec);
+ max98090_configure_bclk(component);
cdata->rate = max98090->lrclk;
/* Update filter mode */
if (max98090->lrclk < 24000)
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_MODE_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ 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_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ snd_soc_component_update_bits(component, M98090_REG_FILTER_CONFIG,
M98090_DHF_MASK, 0);
else
- snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+ 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,
@@ -2034,8 +1982,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -2052,19 +2000,19 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq <= 20000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
max98090->pclk = freq;
} else if ((freq > 20000000) && (freq <= 40000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
max98090->pclk = freq >> 1;
} else if ((freq > 40000000) && (freq <= 60000000)) {
- snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+ snd_soc_component_write(component, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
max98090->pclk = freq >> 2;
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -2073,13 +2021,14 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int max98090_dai_mute(struct snd_soc_dai *codec_dai, int mute,
+ int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
int regval;
regval = mute ? M98090_DVM_MASK : 0;
- snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL,
+ snd_soc_component_update_bits(component, M98090_REG_DAI_PLAYBACK_LEVEL,
M98090_DVM_MASK, regval);
return 0;
@@ -2088,14 +2037,14 @@ static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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 && dai->active == 1)
+ 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));
@@ -2103,7 +2052,7 @@ static int max98090_dai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!max98090->master && dai->active == 1)
+ if (!max98090->master && snd_soc_dai_active(dai) == 1)
schedule_work(&max98090->pll_det_disable_work);
break;
default:
@@ -2118,7 +2067,7 @@ 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_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
unsigned int status, mask;
/*
@@ -2141,7 +2090,7 @@ static void max98090_pll_det_enable_work(struct work_struct *work)
msecs_to_jiffies(100));
/* Enable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK,
1 << M98090_IULK_SHIFT);
}
@@ -2150,35 +2099,49 @@ 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_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
cancel_delayed_work_sync(&max98090->pll_det_enable_work);
/* Disable PLL unlock interrupt */
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IULK_MASK, 0);
}
-static void max98090_pll_work(struct work_struct *work)
+static void max98090_pll_work(struct max98090_priv *max98090)
{
- struct max98090_priv *max98090 =
- container_of(work, struct max98090_priv, pll_work);
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
+ unsigned int pll;
+ int i;
- if (!snd_soc_codec_is_active(codec))
+ if (!snd_soc_component_active(component))
return;
- dev_info(codec->dev, "PLL unlocked\n");
+ 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_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, 0);
- msleep(10);
- snd_soc_update_bits(codec, M98090_REG_DEVICE_SHUTDOWN,
+ snd_soc_component_update_bits(component, M98090_REG_DEVICE_SHUTDOWN,
M98090_SHDNN_MASK, M98090_SHDNN_MASK);
- /* Give PLL time to lock */
- msleep(10);
+ 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)
@@ -2186,8 +2149,7 @@ 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_codec *codec = max98090->codec;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_component *component = max98090->component;
int status = 0;
int reg;
@@ -2195,25 +2157,23 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
/* Strong pull up allows mic detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, 0);
msleep(50);
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ snd_soc_component_read(component, M98090_REG_JACK_STATUS);
/* Weak pull up allows only insertion detection */
- snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_update_bits(component, M98090_REG_JACK_DETECT,
M98090_JDWK_MASK, M98090_JDWK_MASK);
- } else {
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
}
- reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+ 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(codec->dev, "No Headset Detected\n");
+ dev_dbg(component->dev, "No Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
@@ -2225,7 +2185,7 @@ static void max98090_jack_work(struct work_struct *work)
if (max98090->jack_state ==
M98090_JACK_STATE_HEADSET) {
- dev_dbg(codec->dev,
+ dev_dbg(component->dev,
"Headset Button Down Detected\n");
/*
@@ -2242,7 +2202,7 @@ static void max98090_jack_work(struct work_struct *work)
/* Line is reported as Headphone */
/* Nokia Headset is reported as Headphone */
/* Mono Headphone is reported as Headphone */
- dev_dbg(codec->dev, "Headphone Detected\n");
+ dev_dbg(component->dev, "Headphone Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
@@ -2251,7 +2211,7 @@ static void max98090_jack_work(struct work_struct *work)
break;
case M98090_JKSNS_MASK:
- dev_dbg(codec->dev, "Headset Detected\n");
+ dev_dbg(component->dev, "Headset Detected\n");
max98090->jack_state = M98090_JACK_STATE_HEADSET;
@@ -2260,34 +2220,32 @@ static void max98090_jack_work(struct work_struct *work)
break;
default:
- dev_dbg(codec->dev, "Unrecognized Jack Status\n");
+ dev_dbg(component->dev, "Unrecognized Jack Status\n");
break;
}
snd_soc_jack_report(max98090->jack, status,
SND_JACK_HEADSET | SND_JACK_BTN_0);
-
- snd_soc_dapm_sync(dapm);
}
static irqreturn_t max98090_interrupt(int irq, void *data)
{
struct max98090_priv *max98090 = data;
- struct snd_soc_codec *codec = max98090->codec;
+ struct snd_soc_component *component = max98090->component;
int ret;
unsigned int mask;
unsigned int active;
/* Treat interrupt before codec is initialized as spurious */
- if (codec == NULL)
+ if (component == NULL)
return IRQ_NONE;
- dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
+ dev_dbg(component->dev, "***** max98090_interrupt *****\n");
ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_INTERRUPT_S: %d\n",
ret);
return IRQ_NONE;
@@ -2296,13 +2254,13 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"failed to read M98090_REG_DEVICE_STATUS: %d\n",
ret);
return IRQ_NONE;
}
- dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
+ dev_dbg(component->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
active, mask, active & mask);
active &= mask;
@@ -2311,20 +2269,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
return IRQ_NONE;
if (active & M98090_CLD_MASK)
- dev_err(codec->dev, "M98090_CLD_MASK\n");
+ dev_err(component->dev, "M98090_CLD_MASK\n");
if (active & M98090_SLD_MASK)
- dev_dbg(codec->dev, "M98090_SLD_MASK\n");
+ dev_dbg(component->dev, "M98090_SLD_MASK\n");
if (active & M98090_ULK_MASK) {
- dev_dbg(codec->dev, "M98090_ULK_MASK\n");
- schedule_work(&max98090->pll_work);
+ dev_dbg(component->dev, "M98090_ULK_MASK\n");
+ max98090_pll_work(max98090);
}
if (active & M98090_JDET_MASK) {
- dev_dbg(codec->dev, "M98090_JDET_MASK\n");
+ dev_dbg(component->dev, "M98090_JDET_MASK\n");
- pm_wakeup_event(codec->dev, 100);
+ pm_wakeup_event(component->dev, 100);
queue_delayed_work(system_power_efficient_wq,
&max98090->jack_work,
@@ -2332,10 +2290,10 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
}
if (active & M98090_DRCACT_MASK)
- dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
+ dev_dbg(component->dev, "M98090_DRCACT_MASK\n");
if (active & M98090_DRCCLP_MASK)
- dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
+ dev_err(component->dev, "M98090_DRCCLP_MASK\n");
return IRQ_HANDLED;
}
@@ -2343,7 +2301,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
/**
* max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
*
- * @codec: MAX98090 codec
+ * @component: MAX98090 component
* @jack: jack to report detection events on
*
* Enable microphone detection via IRQ on the MAX98090. If GPIOs are
@@ -2353,20 +2311,20 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
*
* If no jack is supplied detection will be disabled.
*/
-int max98090_mic_detect(struct snd_soc_codec *codec,
+int max98090_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "max98090_mic_detect\n");
+ dev_dbg(component->dev, "max98090_mic_detect\n");
max98090->jack = jack;
if (jack) {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
1 << M98090_IJDET_SHIFT);
} else {
- snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+ snd_soc_component_update_bits(component, M98090_REG_INTERRUPT_S,
M98090_IJDET_MASK,
0);
}
@@ -2386,17 +2344,18 @@ 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 struct snd_soc_dai_ops max98090_dai_ops = {
+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,
- .digital_mute = max98090_dai_digital_mute,
+ .mute_stream = max98090_dai_mute,
.trigger = max98090_dai_trigger,
+ .no_capture_mute = 1,
};
-static struct snd_soc_dai_driver max98090_dai[] = {
-{
+static struct snd_soc_dai_driver max98090_dai = {
.name = "HiFi",
.playback = {
.stream_name = "HiFi Playback",
@@ -2413,23 +2372,24 @@ static struct snd_soc_dai_driver max98090_dai[] = {
.formats = MAX98090_FORMATS,
},
.ops = &max98090_dai_ops,
-}
};
-static int max98090_probe(struct snd_soc_codec *codec)
+static int max98090_probe(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "max98090_probe\n");
+ dev_dbg(component->dev, "max98090_probe\n");
- max98090->mclk = devm_clk_get(codec->dev, "mclk");
+ max98090->mclk = devm_clk_get(component->dev, "mclk");
if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- max98090->codec = codec;
+ max98090->component = component;
/* Reset the codec, the DSP core, and disable all interrupts */
max98090_reset(max98090);
@@ -2448,26 +2408,29 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090->pa1en = 0;
max98090->pa2en = 0;
- ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
+ max98090->tdm_lslot = 0;
+ max98090->tdm_rslot = 1;
+
+ ret = snd_soc_component_read(component, M98090_REG_REVISION_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failed to read device revision: %d\n",
+ 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(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
devtype = MAX98091;
- dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
+ dev_info(component->dev, "MAX98091 REVID=0x%02x\n", ret);
} else {
devtype = MAX98090;
- dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
+ dev_err(component->dev, "Unrecognized revision 0x%02x\n", ret);
}
if (max98090->devtype != devtype) {
- dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+ dev_warn(component->dev, "Mismatch in DT specified CODEC type.\n");
max98090->devtype = devtype;
}
@@ -2478,10 +2441,9 @@ static int max98090_probe(struct snd_soc_codec *codec)
max98090_pll_det_enable_work);
INIT_WORK(&max98090->pll_det_disable_work,
max98090_pll_det_disable_work);
- INIT_WORK(&max98090->pll_work, max98090_pll_work);
/* Enable jack detection */
- snd_soc_write(codec, M98090_REG_JACK_DETECT,
+ snd_soc_component_write(component, M98090_REG_JACK_DETECT,
M98090_JDETEN_MASK | M98090_JDEB_25MS);
/*
@@ -2489,49 +2451,74 @@ static int max98090_probe(struct snd_soc_codec *codec)
* An old interrupt ocurring prior to installing the ISR
* can keep a new interrupt from generating a trigger.
*/
- snd_soc_read(codec, M98090_REG_DEVICE_STATUS);
+ snd_soc_component_read(component, M98090_REG_DEVICE_STATUS);
/* High Performance is default */
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_DACHP_MASK,
1 << M98090_DACHP_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_DAC_CONTROL,
M98090_PERFMODE_MASK,
0 << M98090_PERFMODE_SHIFT);
- snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL,
+ snd_soc_component_update_bits(component, M98090_REG_ADC_CONTROL,
M98090_ADCHP_MASK,
1 << M98090_ADCHP_SHIFT);
/* Turn on VCM bandgap reference */
- snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
+ snd_soc_component_write(component, M98090_REG_BIAS_CONTROL,
M98090_VCM_MODE_MASK);
- snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
- M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+ 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(codec);
+ max98090_add_widgets(component);
err_access:
return ret;
}
-static int max98090_remove(struct snd_soc_codec *codec)
+static void max98090_remove(struct snd_soc_component *component)
{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+ 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);
- cancel_work_sync(&max98090->pll_work);
- max98090->codec = NULL;
+ max98090->component = NULL;
+}
- return 0;
+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 struct snd_soc_codec_driver soc_codec_dev_max98090 = {
- .probe = max98090_probe,
- .remove = max98090_remove,
- .set_bias_level = max98090_set_bias_level,
+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 = {
@@ -2546,12 +2533,16 @@ static const struct regmap_config max98090_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max98090_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *i2c_id)
+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");
@@ -2561,19 +2552,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
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 if (i2c_id) {
- driver_data = i2c_id->driver_data;
- }
-
- max98090->devtype = driver_data;
+ max98090->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data;
@@ -2598,20 +2577,33 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
return ret;
}
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max98090, max98090_dai,
- ARRAY_SIZE(max98090_dai));
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98090,
+ &max98090_dai, 1);
err_enable:
return ret;
}
-static int max98090_i2c_remove(struct i2c_client *client)
+static void max98090_i2c_shutdown(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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);
@@ -2633,9 +2625,7 @@ static int max98090_runtime_suspend(struct device *dev)
return 0;
}
-#endif
-#ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2653,34 +2643,22 @@ static int max98090_resume(struct device *dev)
return 0;
}
-static int max98090_suspend(struct device *dev)
-{
- 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(max98090_suspend, max98090_resume)
+ RUNTIME_PM_OPS(max98090_runtime_suspend, max98090_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(NULL, max98090_resume)
};
-static const struct i2c_device_id max98090_i2c_id[] = {
- { "max98090", MAX98090 },
- { "max98091", MAX98091 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
-
+#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 struct acpi_device_id max98090_acpi_match[] = {
+static const struct acpi_device_id max98090_acpi_match[] = {
{ "193C9890", MAX98090 },
{ }
};
@@ -2690,12 +2668,12 @@ MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
static struct i2c_driver max98090_i2c_driver = {
.driver = {
.name = "max98090",
- .owner = THIS_MODULE,
- .pm = &max98090_pm,
+ .pm = pm_ptr(&max98090_pm),
.of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
},
- .probe = max98090_i2c_probe,
+ .probe = max98090_i2c_probe,
+ .shutdown = max98090_i2c_shutdown,
.remove = max98090_i2c_remove,
.id_table = max98090_i2c_id,
};
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index 21ff743f5af2..6ce8dd176e48 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98090.h -- MAX98090 ALSA SoC Audio driver
*
* Copyright 2011-2012 Maxim Integrated Products
- *
- * 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 _MAX98090_H
@@ -1519,7 +1516,7 @@ struct max98090_cdata {
struct max98090_priv {
struct regmap *regmap;
- struct snd_soc_codec *codec;
+ struct snd_soc_component *component;
enum max98090_type devtype;
struct max98090_pdata *pdata;
struct clk *mclk;
@@ -1533,19 +1530,20 @@ struct max98090_priv {
struct delayed_work jack_work;
struct delayed_work pll_det_enable_work;
struct work_struct pll_det_disable_work;
- struct work_struct pll_work;
struct snd_soc_jack *jack;
unsigned int dai_fmt;
int tdm_slots;
- int tdm_width;
+ int tdm_lslot;
+ int tdm_rslot;
u8 lin_state;
unsigned int pa1en;
unsigned int pa2en;
unsigned int sidetone;
bool master;
+ bool shdn_pending;
};
-int max98090_mic_detect(struct snd_soc_codec *codec,
+int max98090_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack);
#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 8fba0c3db798..cfb63fe69267 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max98095.c -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 Maxim Integrated Products
- *
- * 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.
*/
#include <linux/module.h>
@@ -202,300 +199,36 @@ static const struct reg_default max98095_reg_def[] = {
{ 0xff, 0x00 }, /* FF */
};
-static struct {
- int readable;
- int writable;
-} max98095_access[M98095_REG_CNT] = {
- { 0x00, 0x00 }, /* 00 */
- { 0xFF, 0x00 }, /* 01 */
- { 0xFF, 0x00 }, /* 02 */
- { 0xFF, 0x00 }, /* 03 */
- { 0xFF, 0x00 }, /* 04 */
- { 0xFF, 0x00 }, /* 05 */
- { 0xFF, 0x00 }, /* 06 */
- { 0xFF, 0x00 }, /* 07 */
- { 0xFF, 0x00 }, /* 08 */
- { 0xFF, 0x00 }, /* 09 */
- { 0xFF, 0x00 }, /* 0A */
- { 0xFF, 0x00 }, /* 0B */
- { 0xFF, 0x00 }, /* 0C */
- { 0xFF, 0x00 }, /* 0D */
- { 0xFF, 0x00 }, /* 0E */
- { 0xFF, 0x9F }, /* 0F */
- { 0xFF, 0xFF }, /* 10 */
- { 0xFF, 0xFF }, /* 11 */
- { 0xFF, 0xFF }, /* 12 */
- { 0xFF, 0xFF }, /* 13 */
- { 0xFF, 0xFF }, /* 14 */
- { 0xFF, 0xFF }, /* 15 */
- { 0xFF, 0xFF }, /* 16 */
- { 0xFF, 0xFF }, /* 17 */
- { 0xFF, 0xFF }, /* 18 */
- { 0xFF, 0xFF }, /* 19 */
- { 0xFF, 0xFF }, /* 1A */
- { 0xFF, 0xFF }, /* 1B */
- { 0xFF, 0xFF }, /* 1C */
- { 0xFF, 0xFF }, /* 1D */
- { 0xFF, 0x77 }, /* 1E */
- { 0xFF, 0x77 }, /* 1F */
- { 0xFF, 0x77 }, /* 20 */
- { 0xFF, 0x77 }, /* 21 */
- { 0xFF, 0x77 }, /* 22 */
- { 0xFF, 0x77 }, /* 23 */
- { 0xFF, 0xFF }, /* 24 */
- { 0xFF, 0x7F }, /* 25 */
- { 0xFF, 0x31 }, /* 26 */
- { 0xFF, 0xFF }, /* 27 */
- { 0xFF, 0xFF }, /* 28 */
- { 0xFF, 0xFF }, /* 29 */
- { 0xFF, 0xF7 }, /* 2A */
- { 0xFF, 0x2F }, /* 2B */
- { 0xFF, 0xEF }, /* 2C */
- { 0xFF, 0xFF }, /* 2D */
- { 0xFF, 0xFF }, /* 2E */
- { 0xFF, 0xFF }, /* 2F */
- { 0xFF, 0xFF }, /* 30 */
- { 0xFF, 0xFF }, /* 31 */
- { 0xFF, 0xFF }, /* 32 */
- { 0xFF, 0xFF }, /* 33 */
- { 0xFF, 0xF7 }, /* 34 */
- { 0xFF, 0x2F }, /* 35 */
- { 0xFF, 0xCF }, /* 36 */
- { 0xFF, 0xFF }, /* 37 */
- { 0xFF, 0xFF }, /* 38 */
- { 0xFF, 0xFF }, /* 39 */
- { 0xFF, 0xFF }, /* 3A */
- { 0xFF, 0xFF }, /* 3B */
- { 0xFF, 0xFF }, /* 3C */
- { 0xFF, 0xFF }, /* 3D */
- { 0xFF, 0xF7 }, /* 3E */
- { 0xFF, 0x2F }, /* 3F */
- { 0xFF, 0xCF }, /* 40 */
- { 0xFF, 0xFF }, /* 41 */
- { 0xFF, 0x77 }, /* 42 */
- { 0xFF, 0xFF }, /* 43 */
- { 0xFF, 0xFF }, /* 44 */
- { 0xFF, 0xFF }, /* 45 */
- { 0xFF, 0xFF }, /* 46 */
- { 0xFF, 0xFF }, /* 47 */
- { 0xFF, 0xFF }, /* 48 */
- { 0xFF, 0x0F }, /* 49 */
- { 0xFF, 0xFF }, /* 4A */
- { 0xFF, 0xFF }, /* 4B */
- { 0xFF, 0x3F }, /* 4C */
- { 0xFF, 0x3F }, /* 4D */
- { 0xFF, 0x3F }, /* 4E */
- { 0xFF, 0xFF }, /* 4F */
- { 0xFF, 0x7F }, /* 50 */
- { 0xFF, 0x7F }, /* 51 */
- { 0xFF, 0x0F }, /* 52 */
- { 0xFF, 0x3F }, /* 53 */
- { 0xFF, 0x3F }, /* 54 */
- { 0xFF, 0x3F }, /* 55 */
- { 0xFF, 0xFF }, /* 56 */
- { 0xFF, 0xFF }, /* 57 */
- { 0xFF, 0xBF }, /* 58 */
- { 0xFF, 0x1F }, /* 59 */
- { 0xFF, 0xBF }, /* 5A */
- { 0xFF, 0x1F }, /* 5B */
- { 0xFF, 0xBF }, /* 5C */
- { 0xFF, 0x3F }, /* 5D */
- { 0xFF, 0x3F }, /* 5E */
- { 0xFF, 0x7F }, /* 5F */
- { 0xFF, 0x7F }, /* 60 */
- { 0xFF, 0x47 }, /* 61 */
- { 0xFF, 0x9F }, /* 62 */
- { 0xFF, 0x9F }, /* 63 */
- { 0xFF, 0x9F }, /* 64 */
- { 0xFF, 0x9F }, /* 65 */
- { 0xFF, 0x9F }, /* 66 */
- { 0xFF, 0xBF }, /* 67 */
- { 0xFF, 0xBF }, /* 68 */
- { 0xFF, 0xFF }, /* 69 */
- { 0xFF, 0xFF }, /* 6A */
- { 0xFF, 0x7F }, /* 6B */
- { 0xFF, 0xF7 }, /* 6C */
- { 0xFF, 0xFF }, /* 6D */
- { 0xFF, 0xFF }, /* 6E */
- { 0xFF, 0x1F }, /* 6F */
- { 0xFF, 0xF7 }, /* 70 */
- { 0xFF, 0xFF }, /* 71 */
- { 0xFF, 0xFF }, /* 72 */
- { 0xFF, 0x1F }, /* 73 */
- { 0xFF, 0xF7 }, /* 74 */
- { 0xFF, 0xFF }, /* 75 */
- { 0xFF, 0xFF }, /* 76 */
- { 0xFF, 0x1F }, /* 77 */
- { 0xFF, 0xF7 }, /* 78 */
- { 0xFF, 0xFF }, /* 79 */
- { 0xFF, 0xFF }, /* 7A */
- { 0xFF, 0x1F }, /* 7B */
- { 0xFF, 0xF7 }, /* 7C */
- { 0xFF, 0xFF }, /* 7D */
- { 0xFF, 0xFF }, /* 7E */
- { 0xFF, 0x1F }, /* 7F */
- { 0xFF, 0xF7 }, /* 80 */
- { 0xFF, 0xFF }, /* 81 */
- { 0xFF, 0xFF }, /* 82 */
- { 0xFF, 0x1F }, /* 83 */
- { 0xFF, 0x7F }, /* 84 */
- { 0xFF, 0x0F }, /* 85 */
- { 0xFF, 0xD8 }, /* 86 */
- { 0xFF, 0xFF }, /* 87 */
- { 0xFF, 0xEF }, /* 88 */
- { 0xFF, 0xFE }, /* 89 */
- { 0xFF, 0xFE }, /* 8A */
- { 0xFF, 0xFF }, /* 8B */
- { 0xFF, 0xFF }, /* 8C */
- { 0xFF, 0x3F }, /* 8D */
- { 0xFF, 0xFF }, /* 8E */
- { 0xFF, 0x3F }, /* 8F */
- { 0xFF, 0x8F }, /* 90 */
- { 0xFF, 0xFF }, /* 91 */
- { 0xFF, 0x3F }, /* 92 */
- { 0xFF, 0xFF }, /* 93 */
- { 0xFF, 0xFF }, /* 94 */
- { 0xFF, 0x0F }, /* 95 */
- { 0xFF, 0x3F }, /* 96 */
- { 0xFF, 0x8C }, /* 97 */
- { 0x00, 0x00 }, /* 98 */
- { 0x00, 0x00 }, /* 99 */
- { 0x00, 0x00 }, /* 9A */
- { 0x00, 0x00 }, /* 9B */
- { 0x00, 0x00 }, /* 9C */
- { 0x00, 0x00 }, /* 9D */
- { 0x00, 0x00 }, /* 9E */
- { 0x00, 0x00 }, /* 9F */
- { 0x00, 0x00 }, /* A0 */
- { 0x00, 0x00 }, /* A1 */
- { 0x00, 0x00 }, /* A2 */
- { 0x00, 0x00 }, /* A3 */
- { 0x00, 0x00 }, /* A4 */
- { 0x00, 0x00 }, /* A5 */
- { 0x00, 0x00 }, /* A6 */
- { 0x00, 0x00 }, /* A7 */
- { 0x00, 0x00 }, /* A8 */
- { 0x00, 0x00 }, /* A9 */
- { 0x00, 0x00 }, /* AA */
- { 0x00, 0x00 }, /* AB */
- { 0x00, 0x00 }, /* AC */
- { 0x00, 0x00 }, /* AD */
- { 0x00, 0x00 }, /* AE */
- { 0x00, 0x00 }, /* AF */
- { 0x00, 0x00 }, /* B0 */
- { 0x00, 0x00 }, /* B1 */
- { 0x00, 0x00 }, /* B2 */
- { 0x00, 0x00 }, /* B3 */
- { 0x00, 0x00 }, /* B4 */
- { 0x00, 0x00 }, /* B5 */
- { 0x00, 0x00 }, /* B6 */
- { 0x00, 0x00 }, /* B7 */
- { 0x00, 0x00 }, /* B8 */
- { 0x00, 0x00 }, /* B9 */
- { 0x00, 0x00 }, /* BA */
- { 0x00, 0x00 }, /* BB */
- { 0x00, 0x00 }, /* BC */
- { 0x00, 0x00 }, /* BD */
- { 0x00, 0x00 }, /* BE */
- { 0x00, 0x00 }, /* BF */
- { 0x00, 0x00 }, /* C0 */
- { 0x00, 0x00 }, /* C1 */
- { 0x00, 0x00 }, /* C2 */
- { 0x00, 0x00 }, /* C3 */
- { 0x00, 0x00 }, /* C4 */
- { 0x00, 0x00 }, /* C5 */
- { 0x00, 0x00 }, /* C6 */
- { 0x00, 0x00 }, /* C7 */
- { 0x00, 0x00 }, /* C8 */
- { 0x00, 0x00 }, /* C9 */
- { 0x00, 0x00 }, /* CA */
- { 0x00, 0x00 }, /* CB */
- { 0x00, 0x00 }, /* CC */
- { 0x00, 0x00 }, /* CD */
- { 0x00, 0x00 }, /* CE */
- { 0x00, 0x00 }, /* CF */
- { 0x00, 0x00 }, /* D0 */
- { 0x00, 0x00 }, /* D1 */
- { 0x00, 0x00 }, /* D2 */
- { 0x00, 0x00 }, /* D3 */
- { 0x00, 0x00 }, /* D4 */
- { 0x00, 0x00 }, /* D5 */
- { 0x00, 0x00 }, /* D6 */
- { 0x00, 0x00 }, /* D7 */
- { 0x00, 0x00 }, /* D8 */
- { 0x00, 0x00 }, /* D9 */
- { 0x00, 0x00 }, /* DA */
- { 0x00, 0x00 }, /* DB */
- { 0x00, 0x00 }, /* DC */
- { 0x00, 0x00 }, /* DD */
- { 0x00, 0x00 }, /* DE */
- { 0x00, 0x00 }, /* DF */
- { 0x00, 0x00 }, /* E0 */
- { 0x00, 0x00 }, /* E1 */
- { 0x00, 0x00 }, /* E2 */
- { 0x00, 0x00 }, /* E3 */
- { 0x00, 0x00 }, /* E4 */
- { 0x00, 0x00 }, /* E5 */
- { 0x00, 0x00 }, /* E6 */
- { 0x00, 0x00 }, /* E7 */
- { 0x00, 0x00 }, /* E8 */
- { 0x00, 0x00 }, /* E9 */
- { 0x00, 0x00 }, /* EA */
- { 0x00, 0x00 }, /* EB */
- { 0x00, 0x00 }, /* EC */
- { 0x00, 0x00 }, /* ED */
- { 0x00, 0x00 }, /* EE */
- { 0x00, 0x00 }, /* EF */
- { 0x00, 0x00 }, /* F0 */
- { 0x00, 0x00 }, /* F1 */
- { 0x00, 0x00 }, /* F2 */
- { 0x00, 0x00 }, /* F3 */
- { 0x00, 0x00 }, /* F4 */
- { 0x00, 0x00 }, /* F5 */
- { 0x00, 0x00 }, /* F6 */
- { 0x00, 0x00 }, /* F7 */
- { 0x00, 0x00 }, /* F8 */
- { 0x00, 0x00 }, /* F9 */
- { 0x00, 0x00 }, /* FA */
- { 0x00, 0x00 }, /* FB */
- { 0x00, 0x00 }, /* FC */
- { 0x00, 0x00 }, /* FD */
- { 0x00, 0x00 }, /* FE */
- { 0xFF, 0x00 }, /* FF */
-};
-
static bool max98095_readable(struct device *dev, unsigned int reg)
{
- if (reg >= M98095_REG_CNT)
- return 0;
- return max98095_access[reg].readable != 0;
+ 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_volatile(struct device *dev, unsigned int reg)
+static bool max98095_writeable(struct device *dev, unsigned int reg)
{
- if (reg > M98095_REG_MAX_CACHED)
- return 1;
-
switch (reg) {
- case M98095_000_HOST_DATA:
- case M98095_001_HOST_INT_STS:
- case M98095_002_HOST_RSP_STS:
- case M98095_003_HOST_CMD_STS:
- case M98095_004_CODEC_STS:
- case M98095_005_DAI1_ALC_STS:
- case M98095_006_DAI2_ALC_STS:
- case M98095_007_JACK_AUTO_STS:
- case M98095_008_JACK_MANUAL_STS:
- case M98095_009_JACK_VBAT_STS:
- case M98095_00A_ACC_ADC_STS:
- case M98095_00B_MIC_NG_AGC_STS:
- case M98095_00C_SPK_L_VOLT_STS:
- case M98095_00D_SPK_R_VOLT_STS:
- case M98095_00E_TEMP_SENSOR_STS:
- return 1;
+ case M98095_00F_HOST_CFG ... M98095_097_PWR_SYS:
+ return true;
+ default:
+ return false;
}
+}
- return 0;
+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 = {
@@ -508,13 +241,14 @@ static const struct regmap_config max98095_regmap = {
.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_codec *codec, unsigned int dai,
+static void m98095_eq_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int eq_reg;
@@ -532,15 +266,15 @@ static void m98095_eq_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, eq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, eq_reg++, M98095_BYTE0(coefs[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_codec *codec, unsigned int dai,
+static void m98095_biquad_band(struct snd_soc_component *component, unsigned int dai,
unsigned int band, u16 *coefs)
{
unsigned int bq_reg;
@@ -558,8 +292,8 @@ static void m98095_biquad_band(struct snd_soc_codec *codec, unsigned int dai,
/* Step through the registers and coefs */
for (i = 0; i < M98095_COEFS_PER_BAND; i++) {
- snd_soc_write(codec, bq_reg++, M98095_BYTE1(coefs[i]));
- snd_soc_write(codec, bq_reg++, M98095_BYTE0(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE1(coefs[i]));
+ snd_soc_component_write(component, bq_reg++, M98095_BYTE0(coefs[i]));
}
}
@@ -616,12 +350,12 @@ static SOC_ENUM_SINGLE_DECL(max98095_dai3_dac_filter_enum,
static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_05F_LVL_MIC1, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -630,8 +364,8 @@ static int max98095_mic1pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98095->mic1pre;
return 0;
@@ -640,12 +374,12 @@ static int max98095_mic1pre_get(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, M98095_060_LVL_MIC2, M98095_MICPRE_MASK,
(1+sel)<<M98095_MICPRE_SHIFT);
return 0;
@@ -654,55 +388,50 @@ static int max98095_mic2pre_set(struct snd_kcontrol *kcontrol,
static int max98095_mic2pre_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = max98095->mic2pre;
return 0;
}
-static const unsigned int max98095_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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 unsigned int max98095_hp_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0)
+);
-static const unsigned int max98095_spk_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+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),
-};
+ 28, 39, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
-static const unsigned int max98095_rcv_lout_tlv[] = {
- TLV_DB_RANGE_HEAD(5),
+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),
-};
+ 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0)
+);
-static const unsigned int max98095_lin_tlv[] = {
- TLV_DB_RANGE_HEAD(3),
+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),
-};
+ 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0)
+);
static const struct snd_kcontrol_new max98095_snd_controls[] = {
@@ -866,21 +595,21 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
static int max98095_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
(1+max98095->mic1pre)<<M98095_MICPRE_SHIFT);
} else {
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK,
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK,
(1+max98095->mic2pre)<<M98095_MICPRE_SHIFT);
}
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec, w->reg, M98095_MICPRE_MASK, 0);
+ snd_soc_component_update_bits(component, w->reg, M98095_MICPRE_MASK, 0);
break;
default:
return -EINVAL;
@@ -896,8 +625,8 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
static int max98095_line_pga(struct snd_soc_dapm_widget *w,
int event, u8 channel)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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)))
@@ -908,13 +637,13 @@ static int max98095_line_pga(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
*state |= channel;
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << w->shift), 0);
}
break;
@@ -944,15 +673,15 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, w->reg,
+ 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_update_bits(codec, w->reg,
+ snd_soc_component_update_bits(component, w->reg,
(1 << (w->shift+2)), 0);
break;
default:
@@ -1210,8 +939,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1223,11 +952,11 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ snd_soc_component_update_bits(component, M98095_02A_DAI1_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1237,31 +966,31 @@ static int max98095_dai1_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_027_DAI1_CLKMODE,
+ 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_read(codec, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_02A_DAI1_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ 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_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_028_DAI1_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_02E_DAI1_FILTERS,
+ snd_soc_component_update_bits(component, M98095_02E_DAI1_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1271,8 +1000,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1284,11 +1013,11 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ snd_soc_component_update_bits(component, M98095_034_DAI2_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1298,31 +1027,31 @@ static int max98095_dai2_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_031_DAI2_CLKMODE,
+ 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_read(codec, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_034_DAI2_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ 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_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_032_DAI2_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_038_DAI2_FILTERS,
+ snd_soc_component_update_bits(component, M98095_038_DAI2_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1332,8 +1061,8 @@ 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_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1345,11 +1074,11 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 16:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, 0);
break;
case 24:
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ snd_soc_component_update_bits(component, M98095_03E_DAI3_FORMAT,
M98095_DAI_WS, M98095_DAI_WS);
break;
default:
@@ -1359,31 +1088,31 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
if (rate_value(rate, &regval))
return -EINVAL;
- snd_soc_update_bits(codec, M98095_03B_DAI3_CLKMODE,
+ 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_read(codec, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
+ if (snd_soc_component_read(component, M98095_03E_DAI3_FORMAT) & M98095_DAI_MAS) {
if (max98095->sysclk == 0) {
- dev_err(codec->dev, "Invalid system clock frequency\n");
+ 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_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ snd_soc_component_write(component, M98095_03C_DAI3_CLKCFG_HI,
(ni >> 8) & 0x7F);
- snd_soc_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
ni & 0xFF);
}
/* Update sample rate mode */
if (rate < 50000)
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, 0);
else
- snd_soc_update_bits(codec, M98095_042_DAI3_FILTERS,
+ snd_soc_component_update_bits(component, M98095_042_DAI3_FILTERS,
M98095_DAI_DHF, M98095_DAI_DHF);
return 0;
@@ -1392,8 +1121,8 @@ static int max98095_dai3_hw_params(struct snd_pcm_substream *substream,
static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -1410,13 +1139,13 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
if ((freq >= 10000000) && (freq < 20000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x10);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x10);
} else if ((freq >= 20000000) && (freq < 40000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x20);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x20);
} else if ((freq >= 40000000) && (freq < 60000000)) {
- snd_soc_write(codec, M98095_026_SYS_CLK, 0x30);
+ snd_soc_component_write(component, M98095_026_SYS_CLK, 0x30);
} else {
- dev_err(codec->dev, "Invalid master clock frequency\n");
+ dev_err(component->dev, "Invalid master clock frequency\n");
return -EINVAL;
}
@@ -1429,8 +1158,8 @@ static int max98095_dai_set_sysclk(struct snd_soc_dai *dai,
static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1439,22 +1168,20 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_028_DAI1_CLKCFG_HI,
+ 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_write(codec, M98095_029_DAI1_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_029_DAI1_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1484,11 +1211,11 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_02A_DAI1_FORMAT,
+ 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_write(codec, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
+ snd_soc_component_write(component, M98095_02B_DAI1_CLOCK, M98095_DAI_BSEL64);
}
return 0;
@@ -1497,8 +1224,8 @@ static int max98095_dai1_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1507,22 +1234,20 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_032_DAI2_CLKCFG_HI,
+ 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_write(codec, M98095_033_DAI2_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_033_DAI2_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1552,11 +1277,11 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_034_DAI2_FORMAT,
+ 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_write(codec, M98095_035_DAI2_CLOCK,
+ snd_soc_component_write(component, M98095_035_DAI2_CLOCK,
M98095_DAI_BSEL64);
}
@@ -1566,8 +1291,8 @@ static int max98095_dai2_set_fmt(struct snd_soc_dai *codec_dai,
static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1576,22 +1301,20 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
if (fmt != cdata->fmt) {
cdata->fmt = fmt;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- /* Slave mode PLL */
- snd_soc_write(codec, M98095_03C_DAI3_CLKCFG_HI,
+ 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_write(codec, M98095_03D_DAI3_CLKCFG_LO,
+ snd_soc_component_write(component, M98095_03D_DAI3_CLKCFG_LO,
0x00);
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- /* Set to master mode */
+ case SND_SOC_DAIFMT_CBP_CFP:
+ /* Set to provider mode */
regval |= M98095_DAI_MAS;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
default:
- dev_err(codec->dev, "Clock mode unsupported");
+ dev_err(component->dev, "Clock mode unsupported");
return -EINVAL;
}
@@ -1621,21 +1344,21 @@ static int max98095_dai3_set_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, M98095_03E_DAI3_FORMAT,
+ 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_write(codec, M98095_03F_DAI3_CLOCK,
+ snd_soc_component_write(component, M98095_03F_DAI3_CLOCK,
M98095_DAI_BSEL64);
}
return 0;
}
-static int max98095_set_bias_level(struct snd_soc_codec *codec,
+static int max98095_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -1650,35 +1373,38 @@ static int max98095_set_bias_level(struct snd_soc_codec *codec,
* away from ON. Disable the clock in that case, otherwise
* enable it.
*/
- if (!IS_ERR(max98095->mclk)) {
- if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
- clk_disable_unprepare(max98095->mclk);
- else
- clk_prepare_enable(max98095->mclk);
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max98095->regmap);
if (ret != 0) {
- dev_err(codec->dev, "Failed to sync cache: %d\n", ret);
+ dev_err(component->dev, "Failed to sync cache: %d\n", ret);
return ret;
}
}
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, M98095_MBEN);
break;
case SND_SOC_BIAS_OFF:
- snd_soc_update_bits(codec, M98095_090_PWR_EN_IN,
+ snd_soc_component_update_bits(component, M98095_090_PWR_EN_IN,
M98095_MBEN, 0);
regcache_mark_dirty(max98095->regmap);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1759,12 +1485,12 @@ static int max98095_get_eq_channel(const char *name)
static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
struct max98095_eq_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
@@ -1793,7 +1519,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->eq_cfg[best].name,
pdata->eq_cfg[best].rate, fs);
@@ -1802,29 +1528,29 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_EQ1EN : M98095_EQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ 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_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_eq_band(codec, channel, 0, coef_set->band1);
- m98095_eq_band(codec, channel, 1, coef_set->band2);
- m98095_eq_band(codec, channel, 2, coef_set->band3);
- m98095_eq_band(codec, channel, 3, coef_set->band4);
- m98095_eq_band(codec, channel, 4, coef_set->band5);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ 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_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1834,9 +1560,9 @@ static int max98095_get_eq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_eq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_eq_cfg *cfg;
unsigned int cfgcnt;
@@ -1889,36 +1615,33 @@ static void max98095_handle_eq_pdata(struct snd_soc_codec *codec)
max98095->eq_enum.texts = max98095->eq_texts;
max98095->eq_enum.items = max98095->eq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add EQ control: %d\n", ret);
+ 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_codec *codec,
+static int max98095_get_bq_channel(struct snd_soc_component *component,
const char *name)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bq_mode_name); i++)
- if (strcmp(name, bq_mode_name[i]) == 0)
- return i;
+ int ret;
- /* Shouldn't happen */
- dev_err(codec->dev, "Bad biquad channel name '%s'\n", name);
- return -EINVAL;
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, kcontrol->id.name);
+ int channel = max98095_get_bq_channel(component, kcontrol->id.name);
struct max98095_cdata *cdata;
- unsigned int sel = ucontrol->value.integer.value[0];
+ unsigned int sel = ucontrol->value.enumerated.item[0];
struct max98095_biquad_cfg *coef_set;
int fs, best, best_val, i;
int regmask, regsave;
@@ -1947,7 +1670,7 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
}
}
- dev_dbg(codec->dev, "Selected %s/%dHz for %dHz sample rate\n",
+ dev_dbg(component->dev, "Selected %s/%dHz for %dHz sample rate\n",
pdata->bq_cfg[best].name,
pdata->bq_cfg[best].rate, fs);
@@ -1956,27 +1679,27 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
regmask = (channel == 0) ? M98095_BQ1EN : M98095_BQ2EN;
/* Disable filter while configuring, and save current on/off state */
- regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
- snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
+ 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_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
- m98095_biquad_band(codec, channel, 0, coef_set->band1);
- m98095_biquad_band(codec, channel, 1, coef_set->band2);
- snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
+ 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_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- int channel = max98095_get_bq_channel(codec, kcontrol->id.name);
+ 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)
@@ -1988,9 +1711,9 @@ static int max98095_get_bq_enum(struct snd_kcontrol *kcontrol,
return 0;
}
-static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_bq_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
struct max98095_biquad_cfg *cfg;
unsigned int cfgcnt;
@@ -2044,19 +1767,19 @@ static void max98095_handle_bq_pdata(struct snd_soc_codec *codec)
max98095->bq_enum.texts = max98095->bq_texts;
max98095->bq_enum.items = max98095->bq_textcnt;
- ret = snd_soc_add_codec_controls(codec, controls, ARRAY_SIZE(controls));
+ ret = snd_soc_add_component_controls(component, controls, ARRAY_SIZE(controls));
if (ret != 0)
- dev_err(codec->dev, "Failed to add Biquad control: %d\n", ret);
+ dev_err(component->dev, "Failed to add Biquad control: %d\n", ret);
}
-static void max98095_handle_pdata(struct snd_soc_codec *codec)
+static void max98095_handle_pdata(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
struct max98095_pdata *pdata = max98095->pdata;
u8 regval = 0;
if (!pdata) {
- dev_dbg(codec->dev, "No platform data\n");
+ dev_dbg(component->dev, "No platform data\n");
return;
}
@@ -2067,27 +1790,27 @@ static void max98095_handle_pdata(struct snd_soc_codec *codec)
if (pdata->digmic_right_mode)
regval |= M98095_DIGMIC_R;
- snd_soc_write(codec, M98095_087_CFG_MIC, regval);
+ snd_soc_component_write(component, M98095_087_CFG_MIC, regval);
/* Configure equalizers */
if (pdata->eq_cfgcnt)
- max98095_handle_eq_pdata(codec);
+ max98095_handle_eq_pdata(component);
/* Configure bi-quad filters */
if (pdata->bq_cfgcnt)
- max98095_handle_bq_pdata(codec);
+ max98095_handle_bq_pdata(component);
}
static irqreturn_t max98095_report_jack(int irq, void *data)
{
- struct snd_soc_codec *codec = data;
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, M98095_007_JACK_AUTO_STS);
+ 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)
@@ -2118,9 +1841,9 @@ static irqreturn_t max98095_report_jack(int irq, void *data)
return IRQ_HANDLED;
}
-static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_enable(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -2131,41 +1854,41 @@ static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
if (max98095->pdata->jack_detect_delay)
slew = max98095->pdata->jack_detect_delay;
- ret = snd_soc_write(codec, M98095_08E_JACK_DC_SLEW, slew);
+ ret = snd_soc_component_write(component, M98095_08E_JACK_DC_SLEW, slew);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
/* configure auto detection to be enabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, detect_enable);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, detect_enable);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ 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_codec *codec)
+static int max98095_jack_detect_disable(struct snd_soc_component *component)
{
int ret = 0;
/* configure auto detection to be disabled */
- ret = snd_soc_write(codec, M98095_089_JACK_DET_AUTO, 0x0);
+ ret = snd_soc_component_write(component, M98095_089_JACK_DET_AUTO, 0x0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg auto detect %d\n", ret);
+ dev_err(component->dev, "Failed to cfg auto detect %d\n", ret);
return ret;
}
return ret;
}
-int max98095_jack_detect(struct snd_soc_codec *codec,
+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_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ 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;
@@ -2175,44 +1898,44 @@ int max98095_jack_detect(struct snd_soc_codec *codec,
if (!hp_jack && !mic_jack)
return -EINVAL;
- max98095_jack_detect_enable(codec);
+ max98095_jack_detect_enable(component);
/* enable interrupts for headphone jack detection */
- ret = snd_soc_update_bits(codec, M98095_013_JACK_INT_EN,
+ ret = snd_soc_component_update_bits(component, M98095_013_JACK_INT_EN,
M98095_IDDONE, M98095_IDDONE);
if (ret < 0) {
- dev_err(codec->dev, "Failed to cfg jack irqs %d\n", ret);
+ dev_err(component->dev, "Failed to cfg jack irqs %d\n", ret);
return ret;
}
- max98095_report_jack(client->irq, codec);
+ max98095_report_jack(client->irq, component);
return 0;
}
EXPORT_SYMBOL_GPL(max98095_jack_detect);
#ifdef CONFIG_PM
-static int max98095_suspend(struct snd_soc_codec *codec)
+static int max98095_suspend(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
if (max98095->headphone_jack || max98095->mic_jack)
- max98095_jack_detect_disable(codec);
+ max98095_jack_detect_disable(component);
- max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
return 0;
}
-static int max98095_resume(struct snd_soc_codec *codec)
+static int max98095_resume(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ struct max98095_priv *max98095 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *client = to_i2c_client(component->dev);
- max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY);
if (max98095->headphone_jack || max98095->mic_jack) {
- max98095_jack_detect_enable(codec);
- max98095_report_jack(client->irq, codec);
+ max98095_jack_detect_enable(component);
+ max98095_report_jack(client->irq, component);
}
return 0;
@@ -2222,30 +1945,30 @@ static int max98095_resume(struct snd_soc_codec *codec)
#define max98095_resume NULL
#endif
-static int max98095_reset(struct snd_soc_codec *codec)
+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_write(codec, M98095_00F_HOST_CFG, 0);
+ ret = snd_soc_component_write(component, M98095_00F_HOST_CFG, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset DSP: %d\n", ret);
+ dev_err(component->dev, "Failed to reset DSP: %d\n", ret);
return ret;
}
- ret = snd_soc_write(codec, M98095_097_PWR_SYS, 0);
+ ret = snd_soc_component_write(component, M98095_097_PWR_SYS, 0);
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
+ 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_write(codec, i, snd_soc_read(codec, i));
+ ret = snd_soc_component_write(component, i, snd_soc_component_read(component, i));
if (ret < 0) {
- dev_err(codec->dev, "Failed to reset: %d\n", ret);
+ dev_err(component->dev, "Failed to reset: %d\n", ret);
return ret;
}
}
@@ -2253,21 +1976,21 @@ static int max98095_reset(struct snd_soc_codec *codec)
return ret;
}
-static int max98095_probe(struct snd_soc_codec *codec)
+static int max98095_probe(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "mclk");
+ 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(codec);
+ max98095_reset(component);
- client = to_i2c_client(codec->dev);
+ client = to_i2c_client(component->dev);
/* initialize private data */
@@ -2301,88 +2024,94 @@ static int max98095_probe(struct snd_soc_codec *codec)
/* register an audio interrupt */
ret = request_threaded_irq(client->irq, NULL,
max98095_report_jack,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "max98095", codec);
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
+ IRQF_ONESHOT, "max98095", component);
if (ret) {
- dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+ dev_err(component->dev, "Failed to request IRQ: %d\n", ret);
goto err_access;
}
}
- ret = snd_soc_read(codec, M98095_0FF_REV_ID);
+ ret = snd_soc_component_read(component, M98095_0FF_REV_ID);
if (ret < 0) {
- dev_err(codec->dev, "Failure reading hardware revision: %d\n",
+ dev_err(component->dev, "Failure reading hardware revision: %d\n",
ret);
goto err_irq;
}
- dev_info(codec->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
+ dev_info(component->dev, "Hardware revision: %c\n", ret - 0x40 + 'A');
- snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
+ snd_soc_component_write(component, M98095_097_PWR_SYS, M98095_PWRSV);
- snd_soc_write(codec, M98095_048_MIX_DAC_LR,
+ snd_soc_component_write(component, M98095_048_MIX_DAC_LR,
M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
- snd_soc_write(codec, M98095_049_MIX_DAC_M,
+ snd_soc_component_write(component, M98095_049_MIX_DAC_M,
M98095_DAI2M_TO_DACM|M98095_DAI3M_TO_DACM);
- snd_soc_write(codec, M98095_092_PWR_EN_OUT, M98095_SPK_SPREADSPECTRUM);
- snd_soc_write(codec, M98095_045_CFG_DSP, M98095_DSPNORMAL);
- snd_soc_write(codec, M98095_04E_CFG_HP, M98095_HPNORMAL);
+ 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_write(codec, M98095_02C_DAI1_IOCFG,
+ snd_soc_component_write(component, M98095_02C_DAI1_IOCFG,
M98095_S1NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_036_DAI2_IOCFG,
+ snd_soc_component_write(component, M98095_036_DAI2_IOCFG,
M98095_S2NORMAL|M98095_SDATA);
- snd_soc_write(codec, M98095_040_DAI3_IOCFG,
+ snd_soc_component_write(component, M98095_040_DAI3_IOCFG,
M98095_S3NORMAL|M98095_SDATA);
- max98095_handle_pdata(codec);
+ max98095_handle_pdata(component);
/* take the codec out of the shut down */
- snd_soc_update_bits(codec, M98095_097_PWR_SYS, M98095_SHDNRUN,
+ 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, codec);
+ free_irq(client->irq, component);
err_access:
return ret;
}
-static int max98095_remove(struct snd_soc_codec *codec)
+static void max98095_remove(struct snd_soc_component *component)
{
- struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
- struct i2c_client *client = to_i2c_client(codec->dev);
+ 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(codec);
+ max98095_jack_detect_disable(component);
if (client->irq)
- free_irq(client->irq, codec);
-
- return 0;
+ free_irq(client->irq, component);
}
-static struct snd_soc_codec_driver soc_codec_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),
+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 int max98095_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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;
@@ -2401,41 +2130,30 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
return ret;
}
- max98095->devtype = id->driver_data;
+ max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98095,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max98095,
max98095_dai, ARRAY_SIZE(max98095_dai));
return ret;
}
-static int max98095_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static const struct i2c_device_id max98095_i2c_id[] = {
- { "max98095", MAX98095 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
-
+#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",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max98095_of_match),
},
- .probe = max98095_i2c_probe,
- .remove = max98095_i2c_remove,
+ .probe = max98095_i2c_probe,
.id_table = max98095_i2c_id,
};
diff --git a/sound/soc/codecs/max98095.h b/sound/soc/codecs/max98095.h
index 2ebbe4e894bf..2af7e77021a2 100644
--- a/sound/soc/codecs/max98095.h
+++ b/sound/soc/codecs/max98095.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max98095.h -- MAX98095 ALSA SoC Audio driver
*
* Copyright 2011 Maxim Integrated Products
- *
- * 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 _MAX98095_H
@@ -315,7 +312,7 @@
/* Default Delay used in Slew Rate Calculation for Jack detection */
#define M98095_DEFAULT_SLEW_DELAY 0x18
-extern int max98095_jack_detect(struct snd_soc_codec *codec,
+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
index 1806333ea29e..cc811f58c9d2 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -1,91 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* max98357a.c -- MAX98357A ALSA SoC Codec driver
*/
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
-#define DRV_NAME "max98357a"
+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 gpio_desc *sdmode = snd_soc_dai_get_drvdata(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:
- gpiod_set_value(sdmode, 1);
+ 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(sdmode, 0);
+ 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_DAC("SDMode", NULL, SND_SOC_NOPM, 0, 0),
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[] = {
- {"Speaker", NULL, "SDMode"},
+ {"SD_MODE", NULL, "HiFi Playback"},
+ {"Speaker", NULL, "SD_MODE"},
};
-static int max98357a_codec_probe(struct snd_soc_codec *codec)
-{
- struct gpio_desc *sdmode;
-
- sdmode = devm_gpiod_get(codec->dev, "sdmode");
- if (IS_ERR(sdmode)) {
- dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n",
- __func__, PTR_ERR(sdmode));
- return PTR_ERR(sdmode);
- }
- gpiod_direction_output(sdmode, 0);
- snd_soc_codec_set_drvdata(codec, sdmode);
-
- return 0;
-}
-
-static struct snd_soc_codec_driver max98357a_codec_driver = {
- .probe = max98357a_codec_probe,
+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 struct snd_soc_dai_ops max98357a_dai_ops = {
- .trigger = max98357a_daiops_trigger,
+static const struct snd_soc_dai_ops max98357a_dai_ops = {
+ .trigger = max98357a_daiops_trigger,
};
static struct snd_soc_dai_driver max98357a_dai_driver = {
- .name = DRV_NAME,
+ .name = "HiFi",
.playback = {
- .stream_name = DRV_NAME "-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,
@@ -97,42 +122,61 @@ static struct snd_soc_dai_driver max98357a_dai_driver = {
static int max98357a_platform_probe(struct platform_device *pdev)
{
+ struct max98357a_priv *max98357a;
int ret;
- ret = snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
- &max98357a_dai_driver, 1);
- if (ret)
- dev_err(&pdev->dev, "%s() error registering codec driver: %d\n",
- __func__, ret);
-
- return 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");
+ }
-static int max98357a_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
+ dev_set_drvdata(&pdev->dev, max98357a);
- return 0;
+ 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," DRV_NAME, },
+ { .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 = DRV_NAME,
+ .name = "max98357a",
.of_match_table = of_match_ptr(max98357a_device_id),
+ .acpi_match_table = ACPI_PTR(max98357a_acpi_match),
},
.probe = max98357a_platform_probe,
- .remove = max98357a_platform_remove,
};
module_platform_driver(max98357a_platform_driver);
MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644
index 000000000000..25af78ab30d5
--- /dev/null
+++ b/sound/soc/codecs/max98363.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98363.h"
+
+static const 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, &reg);
+ 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_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 const 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 <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h
new file mode 100644
index 000000000000..2b6743d3a2cf
--- /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 000000000000..852db211ba1e
--- /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 <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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<<MAX98371_GAIN_WIDTH)-1, 0,
+ max98371_gain_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98371_DIGITAL_GAIN, 0,
+ (1<<MAX98371_DIGITAL_GAIN_WIDTH)-1, 1, digital_tlv),
+ SOC_SINGLE_TLV("Speaker DHT Max Volume", MAX98371_GAIN,
+ 0, (1<<MAX98371_DHT_MAX_WIDTH)-1, 0,
+ max98371_dht_max_gain),
+ SOC_SINGLE_TLV("Speaker DHT Min Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_GAIN_WIDTH)-1, 0,
+ max98371_dht_min_gain),
+ SOC_SINGLE_TLV("Speaker DHT Rotation Volume", MAX98371_DHT_GAIN,
+ 0, (1<<MAX98371_DHT_ROT_WIDTH)-1, 0,
+ max98371_dht_rot_gain),
+ SOC_SINGLE("DHT Attack Step", MAX98371_DHT, MAX98371_DHT_STEP, 3, 0),
+ SOC_SINGLE("DHT Attack Rate", MAX98371_DHT, 0, 7, 0),
+ SOC_ENUM("Monomix Select", max98371_monomix),
+ SOC_ENUM("HPF Cutoff", max98371_hpf_cutoff),
+};
+
+static int max98371_dai_set_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->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, &reg);
+ 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" },
+ { }
+};
+
+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 <yesanishhere@gmail.com>");
+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 000000000000..63d9a9de3316
--- /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 000000000000..f58b8c8625a7
--- /dev/null
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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 const 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,
+ }
+};
+
+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;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+ 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);
+
+ /* Check Revision ID */
+ ret = regmap_read(max98373->regmap,
+ MAX98373_R21FF_REV_ID, &reg);
+ 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"},
+ { },
+};
+
+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 = pm_ptr(&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 <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
new file mode 100644
index 000000000000..88ff215f52b3
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -0,0 +1,888 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#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 const 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+ 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_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 const 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",
+ .of_match_table = of_match_ptr(max98373_of_match),
+ .acpi_match_table = ACPI_PTR(max98373_acpi_match),
+ .pm = pm_ptr(&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 <oleg.sherbakov@maximintegrated.com>");
+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 000000000000..2d8033515d34
--- /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 000000000000..33eb4576da23
--- /dev/null
+++ b/sound/soc/codecs/max98373.c
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <sound/tlv.h>
+#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, &reg);
+ 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;
+
+ /* This will assert RESET */
+ max98373->reset = devm_gpiod_get_optional(dev,
+ "maxim,reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(max98373->reset)) {
+ dev_err(dev, "error %ld looking up RESET GPIO line\n",
+ PTR_ERR(max98373->reset));
+ return;
+ }
+
+ /* Cycle reset */
+ if (max98373->reset) {
+ gpiod_set_consumer_name(max98373->reset ,"MAX98373_RESET");
+ gpiod_direction_output(max98373->reset, 1);
+ msleep(50);
+ gpiod_direction_output(max98373->reset, 0);
+ msleep(20);
+ }
+
+ 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 <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
new file mode 100644
index 000000000000..af3b62217497
--- /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;
+ struct gpio_desc *reset;
+ 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 000000000000..076f15a9867e
--- /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 <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98388.h"
+
+static const 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, &reg);
+ 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, &reg);
+ 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);
+ slot_found++;
+ 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, &reg);
+ 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"},
+ { },
+};
+
+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 <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h
new file mode 100644
index 000000000000..77833d181913
--- /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 000000000000..a8a282ff9fc5
--- /dev/null
+++ b/sound/soc/codecs/max98390.c
@@ -0,0 +1,1141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * max98390.c -- MAX98390 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2020 Maxim Integrated Products
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/cdev.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max98390.h"
+
+static const 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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops max98390_pm = {
+ 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, &reg);
+ 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"},
+ {},
+};
+
+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 = pm_ptr(&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 <steves.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98390.h b/sound/soc/codecs/max98390.h
new file mode 100644
index 000000000000..f4d6758ab4c6
--- /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 000000000000..4b4e1fc98a6d
--- /dev/null
+++ b/sound/soc/codecs/max98396.c
@@ -0,0 +1,1915 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98396.h"
+
+static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
+ "avdd",
+ "dvdd",
+ "dvddio",
+};
+
+static const 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 const 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, &reg);
+ if (ret < 0)
+ return -EINVAL;
+ if (format != (reg & format_mask)) {
+ update = true;
+ } else {
+ ret = regmap_read(max98396->regmap,
+ MAX98396_R2042_PCM_CLK_SETUP, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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), &reg);
+ 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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops max98396_pm = {
+ 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), &reg);
+ 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 = pm_ptr(&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 <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98396.h b/sound/soc/codecs/max98396.h
new file mode 100644
index 000000000000..d396aa3e698b
--- /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
index 10f8e47ce2c2..c395132689b4 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9850.c -- codec driver for max9850
*
@@ -6,13 +7,7 @@
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
*
* Initial development of this code was funded by
- * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
+ * MICRONIC Computer Systeme GmbH, https://www.mcsberlin.de/
*/
#include <linux/module.h>
@@ -32,19 +27,6 @@ struct max9850_priv {
unsigned int sysclk;
};
-/* max9850 register cache */
-static const struct reg_default max9850_reg[] = {
- { 2, 0x0c },
- { 3, 0x00 },
- { 4, 0x00 },
- { 5, 0x00 },
- { 6, 0x00 },
- { 7, 0x00 },
- { 8, 0x00 },
- { 9, 0x00 },
- { 10, 0x00 },
-};
-
/* 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)
@@ -52,9 +34,9 @@ static bool max9850_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case MAX9850_STATUSA:
case MAX9850_STATUSB:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -67,13 +49,12 @@ static const struct regmap_config max9850_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static const unsigned int max9850_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+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),
-};
+ 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),
@@ -131,8 +112,8 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
u64 lrclk_div;
u8 sf, da;
@@ -140,14 +121,14 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
/* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
- sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
+ 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_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
- snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+ 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:
@@ -162,7 +143,7 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+ snd_soc_component_update_bits(component, MAX9850_DIGITAL_AUDIO, 0x3, da);
return 0;
}
@@ -170,16 +151,16 @@ static int max9850_hw_params(struct snd_pcm_substream *substream,
static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ 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_write(codec, MAX9850_CLOCK, 0x0);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x0);
else if (freq <= 26000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x4);
else if (freq <= 40000000)
- snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+ snd_soc_component_write(component, MAX9850_CLOCK, 0x8);
else
return -EINVAL;
@@ -189,15 +170,15 @@ static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 da = 0;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ /* 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -235,15 +216,15 @@ static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
/* set da */
- snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+ snd_soc_component_write(component, MAX9850_DIGITAL_AUDIO, da);
return 0;
}
-static int max9850_set_bias_level(struct snd_soc_codec *codec,
+static int max9850_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+ struct max9850_priv *max9850 = snd_soc_component_get_drvdata(component);
int ret;
switch (level) {
@@ -252,10 +233,10 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
ret = regcache_sync(max9850->regmap);
if (ret) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to sync cache: %d\n", ret);
return ret;
}
@@ -264,7 +245,6 @@ static int max9850_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_OFF:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -291,33 +271,34 @@ static struct snd_soc_dai_driver max9850_dai = {
.ops = &max9850_dai_ops,
};
-static int max9850_probe(struct snd_soc_codec *codec)
+static int max9850_probe(struct snd_soc_component *component)
{
/* enable zero-detect */
- snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+ snd_soc_component_update_bits(component, MAX9850_GENERAL_PURPOSE, 1, 1);
/* enable slew-rate control */
- snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+ snd_soc_component_update_bits(component, MAX9850_VOLUME, 0x40, 0x40);
/* set slew-rate 125ms */
- snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+ snd_soc_component_update_bits(component, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
- .probe = max9850_probe,
- .set_bias_level = max9850_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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,
- const struct i2c_device_id *id)
+static int max9850_i2c_probe(struct i2c_client *i2c)
{
struct max9850_priv *max9850;
int ret;
@@ -333,19 +314,13 @@ static int max9850_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max9850);
- ret = snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_max9850, &max9850_dai, 1);
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_max9850, &max9850_dai, 1);
return ret;
}
-static int max9850_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id max9850_i2c_id[] = {
- { "max9850", 0 },
+ { "max9850" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
@@ -353,10 +328,8 @@ MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
static struct i2c_driver max9850_i2c_driver = {
.driver = {
.name = "max9850",
- .owner = THIS_MODULE,
},
.probe = max9850_i2c_probe,
- .remove = max9850_i2c_remove,
.id_table = max9850_i2c_id,
};
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
index 72b1ddb04b0d..da313640b4bf 100644
--- a/sound/soc/codecs/max9850.h
+++ b/sound/soc/codecs/max9850.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* max9850.h -- codec driver for max9850
*
* Copyright (C) 2011 taskit GmbH
* Author: Christian Glindkamp <christian.glindkamp@taskit.de>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _MAX9850_H
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644
index 000000000000..c94142768c81
--- /dev/null
+++ b/sound/soc/codecs/max98504.c
@@ -0,0 +1,382 @@
+// 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 <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <sound/soc.h>
+
+#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 const 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,
+ const unsigned int *tx_slot,
+ unsigned int rx_num,
+ const 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 000000000000..8b2a113b7118
--- /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 000000000000..2bf8976c1828
--- /dev/null
+++ b/sound/soc/codecs/max98520.c
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2021, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <sound/tlv.h>
+#include "max98520.h"
+
+static const 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 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 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 = {
+ 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, &reg);
+ 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"},
+ { },
+};
+
+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 = pm_ptr(&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 <george.song@maximintegrated.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/codecs/max98520.h b/sound/soc/codecs/max98520.h
new file mode 100644
index 000000000000..89a95c25afcf
--- /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 000000000000..716d16daf7d7
--- /dev/null
+++ b/sound/soc/codecs/max9860.c
@@ -0,0 +1,738 @@
+// 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 <peda@axentia.s>
+// Copyright 2016 Axentia Technologies
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#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,
+};
+
+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;
+}
+
+static const struct dev_pm_ops max9860_pm_ops = {
+ 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 = pm_ptr(&max9860_pm_ops),
+ },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max9860.h b/sound/soc/codecs/max9860.h
new file mode 100644
index 000000000000..e07b905eaf50
--- /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 <peda@axentia.s>
+ * 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 000000000000..50db88fce904
--- /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 <ladis@linux-mips.org>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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 (!snd_soc_dapm_widget_name_cmp(w, "ADCL"))
+ adc_dac = MAX9867_ADC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "ADCR"))
+ adc_dac = MAX9867_ADC_RIGHT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "DACL"))
+ adc_dac = MAX9867_DAC_LEFT;
+ else if (!snd_soc_dapm_widget_name_cmp(w, "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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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" },
+ { }
+};
+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 <ladis@linux-mips.org>");
+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 000000000000..b6b880631b13
--- /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<<MAX9867_PSCLK_SHIFT)
+#define MAX9867_PSCLK_10_20 0x1
+#define MAX9867_PSCLK_20_40 0x2
+#define MAX9867_PSCLK_40_60 0x3
+#define MAX9867_AUDIOCLKHIGH 0x06
+#define MAX9867_NI_HIGH_MASK 0x7F
+#define MAX9867_NI_LOW_MASK 0xFE
+#define MAX9867_PLL (1<<7)
+#define MAX9867_AUDIOCLKLOW 0x07
+#define MAX9867_RAPID_LOCK 0x01
+#define MAX9867_IFC1A 0x08
+#define MAX9867_MASTER (1<<7)
+#define MAX9867_I2S_DLY (1<<4)
+#define MAX9867_SDOUT_HIZ (1<<3)
+#define MAX9867_TDM_MODE (1<<2)
+#define MAX9867_WCI_MODE (1<<6)
+#define MAX9867_BCI_MODE (1<<5)
+#define MAX9867_IFC1B 0x09
+#define MAX9867_IFC1B_BCLK_MASK 7
+#define MAX9867_IFC1B_64X 0x01
+#define MAX9867_IFC1B_48X 0x02
+#define MAX9867_IFC1B_PCLK_2 0x04
+#define MAX9867_IFC1B_PCLK_4 0x05
+#define MAX9867_IFC1B_PCLK_8 0x06
+#define MAX9867_IFC1B_PCLK_16 0x07
+#define MAX9867_CODECFLTR 0x0a
+#define MAX9867_CODECFLTR_MODE (1<<7)
+#define MAX9867_SIDETONE 0x0b
+#define MAX9867_DACLEVEL 0x0c
+#define MAX9867_ADCLEVEL 0x0d
+#define MAX9867_LEFTLINELVL 0x0e
+#define MAX9867_RIGHTLINELVL 0x0f
+#define MAX9867_LEFTVOL 0x10
+#define MAX9867_RIGHTVOL 0x11
+#define MAX9867_LEFTMICGAIN 0x12
+#define MAX9867_RIGHTMICGAIN 0x13
+#define MAX9867_INPUTCONFIG 0x14
+#define MAX9867_MICCONFIG 0x15
+#define MAX9867_MODECONFIG 0x16
+#define MAX9867_PWRMAN 0x17
+#define MAX9867_PWRMAN_SHDN (1<<7)
+#define MAX9867_REVISION 0xff
+
+#define MAX9867_CACHEREGNUM 10
+
+#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 29549cdbf4c1..1bd0d4761ca6 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -1,14 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max9877.c -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#include <linux/module.h>
@@ -20,9 +15,7 @@
#include "max9877.h"
-static struct regmap *regmap;
-
-static struct reg_default max9877_regs[] = {
+static const struct reg_default max9877_regs[] = {
{ 0, 0x40 },
{ 1, 0x00 },
{ 2, 0x00 },
@@ -30,19 +23,17 @@ static struct reg_default max9877_regs[] = {
{ 4, 0x49 },
};
-static const unsigned int max9877_pgain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0)
+);
-static const unsigned int max9877_output_tlv[] = {
- TLV_DB_RANGE_HEAD(4),
+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),
-};
+ 24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0)
+);
static const char *max9877_out_mode[] = {
"INA -> SPK",
@@ -123,7 +114,7 @@ static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
{ "HPR", NULL, "SHDN" },
};
-static const struct snd_soc_codec_driver max9877_codec = {
+static const struct snd_soc_component_driver max9877_component_driver = {
.controls = max9877_controls,
.num_controls = ARRAY_SIZE(max9877_controls),
@@ -142,9 +133,9 @@ static const struct regmap_config max9877_regmap = {
.cache_type = REGCACHE_RBTREE,
};
-static int max9877_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int max9877_i2c_probe(struct i2c_client *client)
{
+ struct regmap *regmap;
int i;
regmap = devm_regmap_init_i2c(client, &max9877_regmap);
@@ -155,18 +146,12 @@ static int max9877_i2c_probe(struct i2c_client *client,
for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
- return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0);
-}
-
-static int max9877_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &max9877_component_driver, NULL, 0);
}
static const struct i2c_device_id max9877_i2c_id[] = {
- { "max9877", 0 },
+ { "max9877" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
@@ -174,10 +159,8 @@ MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
static struct i2c_driver max9877_i2c_driver = {
.driver = {
.name = "max9877",
- .owner = THIS_MODULE,
},
.probe = max9877_i2c_probe,
- .remove = max9877_i2c_remove,
.id_table = max9877_i2c_id,
};
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
index 6da72290ac58..3c2788175a71 100644
--- a/sound/soc/codecs/max9877.h
+++ b/sound/soc/codecs/max9877.h
@@ -1,14 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* max9877.h -- amp driver for max9877
*
* Copyright (C) 2009 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
*/
#ifndef _MAX9877_H
@@ -32,6 +27,4 @@
#define MAX9877_BYPASS (1 << 6)
#define MAX9877_SHDN (1 << 7)
-extern int max9877_add_controls(struct snd_soc_codec *codec);
-
#endif
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
new file mode 100644
index 000000000000..66c78051bd09
--- /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 <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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<<M98925_SPK_GAIN_WIDTH)-1, 0,
+ max98925_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98925_GAIN_RAMPING,
+ M98925_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98925_GAIN_RAMPING,
+ M98925_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98925_THRESHOLD,
+ M98925_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98925_THRESHOLD, M98925_ALC_TH_SHIFT,
+ (1<<M98925_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98925_boost_voltage),
+};
+
+/* codec sample rate and n/m dividers parameter table */
+static const struct {
+ int rate;
+ int sr;
+ int divisors[3][2];
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ .divisors = { {1, 375}, {5, 1764}, {1, 384} }
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ .divisors = { {147, 40000}, {1, 256}, {147, 40960} }
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ .divisors = { {1, 250}, {5, 1176}, {1, 256} }
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ .divisors = { {2, 375}, {5, 882}, {1, 192} }
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ .divisors = { {147, 20000}, {1, 128}, {147, 20480} }
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ .divisors = { {1, 125}, {5, 588}, {1, 128} }
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ .divisors = { {4, 375}, {5, 441}, {1, 96} }
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ .divisors = { {147, 10000}, {1, 64}, {147, 10240} }
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ .divisors = { {2, 125}, {5, 294}, {1, 64} }
+ },
+};
+
+static inline int max98925_rate_value(struct snd_soc_component *component,
+ int rate, int clock, int *value, int *n, int *m)
+{
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i].rate >= 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, &reg);
+ 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" },
+ { }
+};
+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 <rdbirt@gmail.com>, Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h
new file mode 100644
index 000000000000..6d55ccad27f9
--- /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 000000000000..ae962bda163e
--- /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 <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
+ max98926_spk_tlv),
+ SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
+ MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
+ MAX98926_ALC_EN_SHIFT, 1, 0),
+ SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
+ MAX98926_ALC_TH_SHIFT,
+ (1<<MAX98926_ALC_TH_WIDTH)-1, 0),
+ SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
+ SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
+ MAX98926_BST_ILIM_SHIFT,
+ (1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
+ max98926_current_tlv),
+ SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
+ SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_1_SHIFT,
+ MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
+ SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
+ MAX98926_PDM_CHANNEL_0_SHIFT,
+ MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
+};
+
+static const struct {
+ int rate;
+ int sr;
+} rate_table[] = {
+ {
+ .rate = 8000,
+ .sr = 0,
+ },
+ {
+ .rate = 11025,
+ .sr = 1,
+ },
+ {
+ .rate = 12000,
+ .sr = 2,
+ },
+ {
+ .rate = 16000,
+ .sr = 3,
+ },
+ {
+ .rate = 22050,
+ .sr = 4,
+ },
+ {
+ .rate = 24000,
+ .sr = 5,
+ },
+ {
+ .rate = 32000,
+ .sr = 6,
+ },
+ {
+ .rate = 44100,
+ .sr = 7,
+ },
+ {
+ .rate = 48000,
+ .sr = 8,
+ },
+};
+
+static void max98926_set_sense_data(struct max98926_priv *max98926)
+{
+ regmap_update_bits(max98926->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, &reg);
+ 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" },
+ { }
+};
+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 <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
new file mode 100644
index 000000000000..d622d5f4384c
--- /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 000000000000..0e9b8970997c
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,914 @@
+// 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 <ryans.lee@maximintegrated.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio/consumer.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static const 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;
+}
+
+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;
+}
+
+static const struct dev_pm_ops max98927_pm = {
+ 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, &reg);
+ 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"},
+ { },
+};
+
+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 = pm_ptr(&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 <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100644
index 000000000000..2353910f5f17
--- /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 <ryans.lee@maximintegrated.com>
+ */
+#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
index 2ffb9a0570dc..086ac97e8386 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -1,24 +1,11 @@
+// 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, http://www.phytec.de
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
- * MA 02110-1301, USA.
+ * Phytec Messtechnik GmbH, https://www.phytec.de
*/
#include <linux/module.h>
#include <linux/device.h>
@@ -107,13 +94,13 @@ 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_codec *codec = dai->codec;
+ 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_update_bits(codec, MC13783_AUDIO_DAC,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_DAC,
0xf << 17, i << 17);
return 0;
}
@@ -126,7 +113,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int rate = params_rate(params);
unsigned int val;
@@ -141,7 +128,7 @@ static int mc13783_pcm_hw_params_codec(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
+ snd_soc_component_update_bits(component, MC13783_AUDIO_CODEC, AUDIO_CODEC_CDCFS8K16K,
val);
return 0;
@@ -160,7 +147,7 @@ static int mc13783_pcm_hw_params_sync(struct snd_pcm_substream *substream,
static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ 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;
@@ -194,21 +181,20 @@ static int mc13783_set_fmt(struct snd_soc_dai *dai, unsigned int fmt,
}
/* DAI clock master masks */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
val |= AUDIO_C_CLK_EN;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
val |= AUDIO_CSM;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- case SND_SOC_DAIFMT_CBS_CFM:
+ default:
return -EINVAL;
}
val |= AUDIO_C_RESET;
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -230,11 +216,11 @@ static int mc13783_set_fmt_sync(struct snd_soc_dai *dai, unsigned int fmt)
return ret;
/*
- * In synchronous mode force the voice codec into slave mode
+ * 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_MASTER_MASK;
- fmt |= SND_SOC_DAIFMT_CBS_CFS;
+ fmt &= ~SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
+ fmt |= SND_SOC_DAIFMT_CBC_CFC;
ret = mc13783_set_fmt(dai, fmt, MC13783_AUDIO_CODEC);
return ret;
@@ -255,7 +241,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir,
unsigned int reg)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int clk;
unsigned int val = 0;
unsigned int mask = AUDIO_CLK(0x7) | AUDIO_CLK_SEL;
@@ -275,7 +261,7 @@ static int mc13783_set_sysclk(struct snd_soc_dai *dai,
val |= AUDIO_CLK(clk);
- snd_soc_update_bits(codec, reg, mask, val);
+ snd_soc_component_update_bits(component, reg, mask, val);
return 0;
}
@@ -308,7 +294,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = SSI_NETWORK_DAC_SLOT_MASK |
SSI_NETWORK_DAC_RXSLOT_MASK;
@@ -344,7 +330,7 @@ static int mc13783_set_tdm_slot_dac(struct snd_soc_dai *dai,
return -EINVAL;
}
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -353,7 +339,7 @@ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
unsigned int mask = 0x3f;
@@ -366,7 +352,7 @@ static int mc13783_set_tdm_slot_codec(struct snd_soc_dai *dai,
val |= (0x00 << 2); /* primary timeslot RX/TX(?) is 0 */
val |= (0x01 << 4); /* secondary timeslot TX is 1 */
- snd_soc_update_bits(codec, MC13783_SSI_NETWORK, mask, val);
+ snd_soc_component_update_bits(component, MC13783_SSI_NETWORK, mask, val);
return 0;
}
@@ -606,9 +592,12 @@ static struct snd_kcontrol_new mc13783_control_list[] = {
SOC_SINGLE("MC2 Capture Bias Switch", MC13783_AUDIO_TX, 1, 1, 0),
};
-static int mc13783_probe(struct snd_soc_codec *codec)
+static int mc13783_probe(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -623,26 +612,24 @@ static int mc13783_probe(struct snd_soc_codec *codec)
AUDIO_SSI_SEL, 0);
else
mc13xxx_reg_rmw(priv->mc13xxx, MC13783_AUDIO_CODEC,
- 0, AUDIO_SSI_SEL);
+ 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,
- 0, AUDIO_SSI_SEL);
+ AUDIO_SSI_SEL, AUDIO_SSI_SEL);
return 0;
}
-static int mc13783_remove(struct snd_soc_codec *codec)
+static void mc13783_remove(struct snd_soc_component *component)
{
- struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
-
- return 0;
}
#define MC13783_RATES_RECORD (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
@@ -650,14 +637,14 @@ static int mc13783_remove(struct snd_soc_codec *codec)
#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
-static struct snd_soc_dai_ops mc13783_ops_dac = {
+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 struct snd_soc_dai_ops mc13783_ops_codec = {
+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,
@@ -698,7 +685,7 @@ static struct snd_soc_dai_driver mc13783_dai_async[] = {
},
};
-static struct snd_soc_dai_ops mc13783_ops_sync = {
+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,
@@ -724,25 +711,22 @@ static struct snd_soc_dai_driver mc13783_dai_sync[] = {
.formats = MC13783_FORMATS,
},
.ops = &mc13783_ops_sync,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
}
};
-static struct regmap *mc13783_get_regmap(struct device *dev)
-{
- return dev_get_regmap(dev->parent, NULL);
-}
-
-static struct snd_soc_codec_driver soc_codec_dev_mc13783 = {
- .probe = mc13783_probe,
- .remove = mc13783_remove,
- .get_regmap = mc13783_get_regmap,
- .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),
+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)
@@ -783,27 +767,19 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
if (priv->adc_ssi_port == priv->dac_ssi_port)
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_sync, ARRAY_SIZE(mc13783_dai_sync));
else
- ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
+ ret = devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
return ret;
}
-static int mc13783_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
-}
-
static struct platform_driver mc13783_codec_driver = {
.driver = {
.name = "mc13783-codec",
},
- .remove = mc13783_codec_remove,
};
module_platform_driver_probe(mc13783_codec_driver, mc13783_codec_probe);
diff --git a/sound/soc/codecs/mc13783.h b/sound/soc/codecs/mc13783.h
index 3a6d1993a217..8992d3ab57e6 100644
--- a/sound/soc/codecs/mc13783.h
+++ b/sound/soc/codecs/mc13783.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef MC13783_MIXER_H
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 711f55039522..c6585e8143a5 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -1,18 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
@@ -68,7 +56,6 @@ 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 DECLARE_TLV_DB_SCALE(ngth, -7650, 150, 0);
static const char * const ml26124_companding[] = {"16bit PCM", "u-law",
"A-law"};
@@ -199,7 +186,7 @@ static const struct clk_coeff coeff_div[] = {
{12288000, 48000, 0xc, 0x0, 0x30, 0x0, 0x4},
};
-static struct reg_default ml26124_reg[] = {
+static const struct reg_default ml26124_reg[] = {
/* CLOCK control Register */
{0x00, 0x00 }, /* Sampling Rate */
{0x02, 0x00}, /* PLL NL */
@@ -338,9 +325,10 @@ 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_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -350,96 +338,59 @@ static int ml26124_hw_params(struct snd_pcm_substream *substream,
if (priv->clk_in) {
switch (priv->mclk / params_rate(hw_params)) {
case 256:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 1);
break;
case 512:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 2);
break;
case 1024:
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 3);
break;
default:
- dev_err(codec->dev, "Unsupported MCLKI\n");
+ dev_err(component->dev, "Unsupported MCLKI\n");
break;
}
} else {
- snd_soc_update_bits(codec, ML26124_CLK_CTL,
+ snd_soc_component_update_bits(component, ML26124_CLK_CTL,
BIT(0) | BIT(1), 0);
}
- switch (params_rate(hw_params)) {
- case 16000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- case 32000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- case 48000:
- snd_soc_update_bits(codec, ML26124_SMPLING_RATE, 0xf,
- get_srate(params_rate(hw_params)));
- snd_soc_update_bits(codec, ML26124_PLLNL, 0xff,
- coeff_div[i].pllnl);
- snd_soc_update_bits(codec, ML26124_PLLNH, 0x1,
- coeff_div[i].pllnh);
- snd_soc_update_bits(codec, ML26124_PLLML, 0xff,
- coeff_div[i].pllml);
- snd_soc_update_bits(codec, ML26124_PLLMH, 0x3f,
- coeff_div[i].pllmh);
- snd_soc_update_bits(codec, ML26124_PLLDIV, 0x1f,
- coeff_div[i].plldiv);
- break;
- default:
- pr_err("%s:this rate is no support for ml26124\n", __func__);
- return -EINVAL;
- }
+ 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)
+static int ml26124_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(0), 1);
break;
case SNDRV_PCM_STREAM_PLAYBACK:
- snd_soc_update_bits(codec, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
+ snd_soc_component_update_bits(component, ML26124_REC_PLYBAK_RUN, BIT(1), 2);
break;
}
if (mute)
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_ON);
else
- snd_soc_update_bits(codec, ML26124_DVOL_CTL, BIT(4),
+ snd_soc_component_update_bits(component, ML26124_DVOL_CTL, BIT(4),
DVOL_CTL_DVMUTE_OFF);
return 0;
@@ -449,20 +400,19 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
unsigned char mode;
- struct snd_soc_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
- /* set master/slave audio interface */
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
mode = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
mode = 0;
break;
default:
return -EINVAL;
}
- snd_soc_update_bits(codec, ML26124_SAI_MODE_SEL, BIT(0), mode);
+ snd_soc_component_update_bits(component, ML26124_SAI_MODE_SEL, BIT(0), mode);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -486,8 +436,8 @@ static int ml26124_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -505,17 +455,17 @@ static int ml26124_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ml26124_set_bias_level(struct snd_soc_codec *codec,
+static int ml26124_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ml26124_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct ml26124_priv *priv = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK, ML26124_BLT_PREAMP_ON);
msleep(100);
- snd_soc_update_bits(codec, ML26124_PW_SPAMP_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_SPAMP_PW_MNG,
ML26124_R26_MASK,
ML26124_MICBEN_ON | ML26124_BLT_ALL_ON);
break;
@@ -523,8 +473,8 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* VMID ON */
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ 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);
@@ -532,19 +482,19 @@ static int ml26124_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
/* VMID OFF */
- snd_soc_update_bits(codec, ML26124_PW_REF_PW_MNG,
+ snd_soc_component_update_bits(component, ML26124_PW_REF_PW_MNG,
ML26124_VMID, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
static const struct snd_soc_dai_ops ml26124_dai_ops = {
.hw_params = ml26124_hw_params,
- .digital_mute = ml26124_mute,
+ .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 = {
@@ -562,28 +512,31 @@ static struct snd_soc_dai_driver ml26124_dai = {
.rates = ML26124_RATES,
.formats = ML26124_FORMATS,},
.ops = &ml26124_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
-static int ml26124_probe(struct snd_soc_codec *codec)
+static int ml26124_probe(struct snd_soc_component *component)
{
/* Software Reset */
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 1);
- snd_soc_update_bits(codec, ML26124_SW_RST, 0x01, 0);
+ 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 struct snd_soc_codec_driver soc_codec_dev_ml26124 = {
- .probe = ml26124_probe,
- .set_bias_level = ml26124_set_bias_level,
- .suspend_bias_off = true,
- .dapm_widgets = ml26124_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ml26124_dapm_widgets),
- .dapm_routes = ml26124_intercon,
- .num_dapm_routes = ARRAY_SIZE(ml26124_intercon),
- .controls = ml26124_snd_controls,
- .num_controls = ARRAY_SIZE(ml26124_snd_controls),
+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 = {
@@ -596,8 +549,7 @@ static const struct regmap_config ml26124_i2c_regmap = {
.write_flag_mask = 0x01,
};
-static int ml26124_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ml26124_i2c_probe(struct i2c_client *i2c)
{
struct ml26124_priv *priv;
int ret;
@@ -615,18 +567,12 @@ static int ml26124_i2c_probe(struct i2c_client *i2c,
return ret;
}
- return snd_soc_register_codec(&i2c->dev,
- &soc_codec_dev_ml26124, &ml26124_dai, 1);
-}
-
-static int ml26124_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ 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 },
+ { "ml26124" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
@@ -634,10 +580,8 @@ MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
static struct i2c_driver ml26124_i2c_driver = {
.driver = {
.name = "ml26124",
- .owner = THIS_MODULE,
},
.probe = ml26124_i2c_probe,
- .remove = ml26124_i2c_remove,
.id_table = ml26124_i2c_id,
};
diff --git a/sound/soc/codecs/ml26124.h b/sound/soc/codecs/ml26124.h
index 5ea0cbb8c46c..080a6232f41f 100644
--- a/sound/soc/codecs/ml26124.h
+++ b/sound/soc/codecs/ml26124.h
@@ -1,18 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2011 LAPIS Semiconductor Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef ML26124_H
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
new file mode 100644
index 000000000000..9ca381812975
--- /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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#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 <srinivas.kandagatla@linaro.org>");
+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 000000000000..ebb6f2e84818
--- /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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#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 = msm8916_wcd_digital_remove,
+};
+
+module_platform_driver(msm8916_wcd_digital_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>");
+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 000000000000..2a5e963fb2b5
--- /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 <kaichieh.chuang@mediatek.com>
+
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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, &reg);
+ 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 <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6351.h b/sound/soc/codecs/mt6351.h
new file mode 100644
index 000000000000..04b2ab694ec7
--- /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 <kaichieh.chuang@mediatek.com>
+ */
+
+#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/mt6357.c b/sound/soc/codecs/mt6357.c
new file mode 100644
index 000000000000..988728df15e4
--- /dev/null
+++ b/sound/soc/codecs/mt6357.c
@@ -0,0 +1,1855 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MT6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include "mt6357.h"
+
+static void set_playback_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_CLR, MT6357_GPIO_MODE2_CLEAR_ALL);
+ if (enable) {
+ /* set gpio mosi mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE2_SET,
+ MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI |
+ MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 |
+ MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 |
+ MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to dir input
+ * reason:
+ * pad_aud_dat_mosi*, because the pin is used as boot strap
+ */
+ regmap_update_bits(priv->regmap, MT6357_GPIO_DIR0,
+ MT6357_GPIO8_DIR_MASK |
+ MT6357_GPIO9_DIR_MASK |
+ MT6357_GPIO10_DIR_MASK |
+ MT6357_GPIO11_DIR_MASK,
+ MT6357_GPIO8_DIR_INPUT |
+ MT6357_GPIO9_DIR_INPUT |
+ MT6357_GPIO10_DIR_INPUT |
+ MT6357_GPIO11_DIR_INPUT);
+ }
+}
+
+static void set_capture_gpio(struct mt6357_priv *priv, bool enable)
+{
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_CLR, MT6357_GPIO_MODE3_CLEAR_ALL);
+ if (enable) {
+ /* set gpio miso mode */
+ regmap_write(priv->regmap, MT6357_GPIO_MODE3_SET,
+ MT6357_GPIO12_MODE_SET_AUD_CLK_MISO |
+ MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 |
+ MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 |
+ MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO);
+ } else {
+ /* pad_aud_*_mosi are GPIO mode after clear and set them to 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, MT6357_GPIO_DIR0,
+ MT6357_GPIO12_DIR_MASK |
+ MT6357_GPIO13_DIR_MASK |
+ MT6357_GPIO14_DIR_MASK |
+ MT6357_GPIO15_DIR_MASK,
+ MT6357_GPIO12_DIR_INPUT |
+ MT6357_GPIO13_DIR_INPUT |
+ MT6357_GPIO14_DIR_INPUT |
+ MT6357_GPIO15_DIR_INPUT);
+ }
+}
+
+static void hp_main_output_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Enable/Reduce HPL/R main output stage step by step */
+ for (i = 0; i <= MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX; i++) {
+ stage = up ? i : MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT);
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_STG_CTRL_VAUDP15_MASK,
+ stage << MT6357_HPROUT_STG_CTRL_VAUDP15_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_aux_feedback_loop_gain_ramp(struct mt6357_priv *priv, bool up)
+{
+ int i, stage;
+
+ /* Reduce HP aux feedback loop gain step by step */
+ for (i = 0; i <= MT6357_HP_AUX_LOOP_GAIN_MAX; i++) {
+ stage = up ? i : MT6357_HP_AUX_LOOP_GAIN_MAX - i;
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_AUX_LOOP_GAIN_MASK,
+ stage << MT6357_HP_AUX_LOOP_GAIN_SFT);
+ usleep_range(600, 700);
+ }
+}
+
+static void hp_pull_down(struct mt6357_priv *priv, bool enable)
+{
+ if (enable)
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE);
+ else
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_MASK,
+ MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE);
+}
+
+static bool is_valid_hp_pga_idx(int reg_idx)
+{
+ return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_12DB) || reg_idx == DL_GAIN_N_40DB;
+}
+
+static void volume_ramp(struct mt6357_priv *priv, int lfrom, int lto,
+ int rfrom, int rto, unsigned int reg_addr)
+{
+ int lcount, rcount, sleep = 0;
+
+ if (!is_valid_hp_pga_idx(lfrom) || !is_valid_hp_pga_idx(lto))
+ pr_debug("%s(), invalid left volume index, from %d, to %d\n",
+ __func__, lfrom, lto);
+
+ if (!is_valid_hp_pga_idx(rfrom) || !is_valid_hp_pga_idx(rto))
+ pr_debug("%s(), invalid right volume index, from %d, to %d\n",
+ __func__, rfrom, rto);
+
+ if (lto > lfrom)
+ lcount = 1;
+ else
+ lcount = -1;
+
+ if (rto > rfrom)
+ rcount = 1;
+ else
+ rcount = -1;
+
+ while ((lto != lfrom) || (rto != rfrom)) {
+ if (lto != lfrom) {
+ lfrom += lcount;
+ if (is_valid_hp_pga_idx(lfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_LEFT_MASK,
+ lfrom << MT6357_DL_GAIN_REG_LEFT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (rto != rfrom) {
+ rfrom += rcount;
+ if (is_valid_hp_pga_idx(rfrom)) {
+ regmap_update_bits(priv->regmap, reg_addr,
+ MT6357_DL_GAIN_REG_RIGHT_MASK,
+ rfrom << MT6357_DL_GAIN_REG_RIGHT_SHIFT);
+ sleep = 1;
+ }
+ }
+ if (sleep)
+ usleep_range(200, 300);
+ }
+}
+
+static void lo_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON1);
+}
+
+static void hp_volume_ramp(struct mt6357_priv *priv, int lfrom, int lto, int rfrom, int rto)
+{
+ volume_ramp(priv, lfrom, lto, rfrom, rto, MT6357_ZCD_CON2);
+}
+
+static void hs_volume_ramp(struct mt6357_priv *priv, int from, int to)
+{
+ volume_ramp(priv, from, to, 0, 0, MT6357_ZCD_CON3);
+}
+
+/* Volume and channel swap controls */
+static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(hp_degain_tlv, -1200, 1200, 0);
+
+static const struct snd_kcontrol_new mt6357_controls[] = {
+ /* dl pga gain */
+ SOC_DOUBLE_TLV("Headphone Volume",
+ MT6357_ZCD_CON2, MT6357_AUD_HPL_GAIN_SFT,
+ MT6357_AUD_HPR_GAIN_SFT, MT6357_AUD_HP_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Headphone Vin Volume",
+ MT6357_AUDDEC_ANA_CON7, MT6357_HP_IVBUF_DEGAIN_SFT,
+ MT6357_HP_IVBUF_DEGAIN_MAX, 1, hp_degain_tlv),
+ SOC_DOUBLE_TLV("Lineout Volume",
+ MT6357_ZCD_CON1, MT6357_AUD_LOL_GAIN_SFT,
+ MT6357_AUD_LOR_GAIN_SFT, MT6357_AUD_LO_GAIN_MAX,
+ 1, playback_tlv),
+ SOC_SINGLE_TLV("Handset Volume",
+ MT6357_ZCD_CON3, MT6357_AUD_HS_GAIN_SFT,
+ MT6357_AUD_HS_GAIN_MAX, 1, playback_tlv),
+ /* ul pga gain */
+ SOC_DOUBLE_R_TLV("Mic Volume",
+ MT6357_AUDENC_ANA_CON0, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPLGAIN_SFT, MT6357_AUDPREAMPLGAIN_MAX,
+ 0, capture_tlv),
+};
+
+/* Uplink controls */
+
+enum {
+ 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,
+ MIC_TYPE_MUX_LPBK,
+ MIC_TYPE_MUX_SGEN,
+};
+
+#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",
+ "Loopback",
+ "Sine Generator",
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_type_mux_map_enum, SND_SOC_NOPM,
+ 0, mic_type_mux_map);
+
+static const struct snd_kcontrol_new mic_type_mux_control =
+ SOC_DAPM_ENUM("Mic Type Select", mic_type_mux_map_enum);
+
+static const char * const pga_mux_map[] = {
+ "None", "AIN0", "AIN1", "AIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(pga_left_mux_map_enum,
+ MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_left_mux_control =
+ SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(pga_right_mux_map_enum,
+ MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRINPUTSEL_SFT,
+ pga_mux_map);
+
+static const struct snd_kcontrol_new pga_right_mux_control =
+ SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum);
+
+/* Downlink controls */
+static const char * const hslo_mux_map[] = {
+ "Open", "DACR", "Playback", "Test mode"
+};
+
+static SOC_ENUM_SINGLE_DECL(lo_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new lo_mux_control =
+ SOC_DAPM_ENUM("Line out source", lo_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hs_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT,
+ hslo_mux_map);
+
+static const struct snd_kcontrol_new hs_mux_control =
+ SOC_DAPM_ENUM("Handset source", hs_mux_map_enum);
+
+static const char * const hplr_mux_map[] = {
+ "Open", "Line Out", "DAC", "Handset"
+};
+
+static SOC_ENUM_SINGLE_DECL(hpr_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpr_mux_control =
+ SOC_DAPM_ENUM("Headphone Right source", hpr_mux_map_enum);
+
+static SOC_ENUM_SINGLE_DECL(hpl_mux_map_enum,
+ MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT,
+ hplr_mux_map);
+
+static const struct snd_kcontrol_new hpl_mux_control =
+ SOC_DAPM_ENUM("Headphone Left source", hpl_mux_map_enum);
+
+static const char * const dac_mux_map[] = {
+ "Normal Path", "Sine Generator"
+};
+
+static SOC_ENUM_SINGLE_DECL(dac_mux_map_enum,
+ MT6357_AFE_TOP_CON0,
+ MT6357_DL_SINE_ON_SFT,
+ dac_mux_map);
+
+static const struct snd_kcontrol_new dac_mux_control =
+ SOC_DAPM_ENUM("DAC Select", dac_mux_map_enum);
+
+static int mt6357_set_dmic(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* DMIC enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_DEFAULT_VALUE | MT6357_AUDDIGMICEN_ENABLE);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_ENABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_SELECT);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* UL turn on SDM 3 level mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SDM_3_LEVEL_CTL_MASK,
+ MT6357_UL_SDM_3_LEVEL_DESELECT);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ /* UL dmic setting: dual mode */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_H,
+ MT6357_C_TWO_DIGITAL_MIC_CTL_MASK,
+ MT6357_C_TWO_DIGITAL_MIC_DISABLE);
+ /* DMIC disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON7,
+ MT6357_AUDDIGMICBIAS_MASK | MT6357_AUDDIGMICEN_MASK,
+ MT6357_AUDDIGMICBIAS_OFF | MT6357_AUDDIGMICEN_DISABLE);
+ }
+ return 0;
+}
+
+static int mt6357_set_amic(struct mt6357_priv *priv, bool enable, unsigned int mic_type)
+{
+ if (enable) {
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_RUN_VALUE);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_OUTPUT);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_ON);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG1,
+ MT6357_DCCLK_RESYNC_BYPASS_MASK,
+ MT6357_DCCLK_RESYNC_BYPASS);
+
+ /* mic bias 0: set the correct DC couple*/
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DCC_ECM_DIFF:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_ALL);
+ break;
+ case MIC_TYPE_MUX_DCC_ECM_SINGLE:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_ENABLE_P1);
+ break;
+ default:
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ break;
+ }
+
+ /* mic bias 1: set the correct DC couple */
+ if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE)
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_ENABLE);
+
+ /* Audio L/R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_ENABLE);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_ENABLE);
+ /* L preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_DC);
+ /* R preamplifier DCCEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_DC);
+ } else {
+ /* Audio L preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* Audio R preamplifier DCC precharge disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK,
+ MT6357_AUDPREAMPRDCCEN_AC);
+ }
+ } else {
+ /* disable any Mic Bias 0 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_DC_MASK,
+ MT6357_AUD_MICBIAS0_DC_DISABLE_ALL);
+ /* disable any Mic Bias 1 DC couple */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK,
+ MT6357_AUD_MICBIAS1_DCSW1P_DISABLE);
+ if (IS_DCC_BASE(mic_type)) {
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_GEN_ON_MASK, MT6357_DCCLK_GEN_OFF);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_PDN_MASK, MT6357_DCCLK_PDN);
+ regmap_update_bits(priv->regmap, MT6357_AFE_DCCLK_CFG0,
+ MT6357_DCCLK_DIV_MASK, MT6357_DCCLK_DIV_STOP_VALUE);
+ }
+ }
+
+ return 0;
+}
+
+static int mt6357_set_loopback(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* enable aud_pad lpk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE);
+ /* Set UL Part: enable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable new lpbk 2 */
+ regmap_update_bits(priv->regmap, MT6357_AFE_ADDA_MTKAIF_CFG0,
+ MT6357_ADDA_MTKAIF_LPBK_CTL_MASK,
+ MT6357_ADDA_MTKAIF_LPBK_DISABLE);
+ /* disable aud_pad lpbk TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_MASK,
+ MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ return 0;
+}
+
+static int mt6357_set_ul_sine_gen(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_ENABLE);
+ } else {
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK,
+ MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ }
+
+ 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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_capture_gpio(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ set_capture_gpio(priv, false);
+ 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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK, MT6357_RSTB_ENCODER_VA28_ENABLE);
+ /* Enable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK, MT6357_LCLDO_ENC_EN_VA28_ENABLE);
+ /* LCLDO_ENC remote sense */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_ENABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* LCLDO_ENC remote sense off */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA28REFGEN_EN_VA28_MASK |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK,
+ MT6357_VA28REFGEN_EN_VA28_DISABLE |
+ MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE);
+ /* disable LCLDO_ENC 2P8V */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_LCLDO_ENC_EN_VA28_MASK,
+ MT6357_LCLDO_ENC_EN_VA28_DISABLE);
+ /* disable audio ADC CLKGEN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_ENCODER_VA28_MASK,
+ MT6357_RSTB_ENCODER_VA28_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int mic_type = dapm_kcontrol_get_value(w->kcontrols[0]);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, true);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, true);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, true);
+ break;
+ default:
+ mt6357_set_amic(priv, true, mic_type);
+ break;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ switch (mic_type) {
+ case MIC_TYPE_MUX_DMIC:
+ mt6357_set_dmic(priv, false);
+ break;
+ case MIC_TYPE_MUX_LPBK:
+ mt6357_set_loopback(priv, false);
+ break;
+ case MIC_TYPE_MUX_SGEN:
+ mt6357_set_ul_sine_gen(priv, false);
+ break;
+ default:
+ mt6357_set_amic(priv, false, mic_type);
+ break;
+ }
+ 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);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* L preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_ENABLE);
+ /* L ADC input sel : L PGA. Enable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRUP);
+ /* Audio L preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio L ADC input sel : off, disable audio L ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLPWRUP_MASK,
+ MT6357_AUDADCLPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCLINPUTSEL_MASK,
+ MT6357_AUDADCLINPUTSEL_IDLE);
+ /* L preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCCEN_MASK,
+ MT6357_AUDPREAMPLDCCEN_AC);
+ /* L preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLON_MASK,
+ MT6357_AUDPREAMPLON_DISABLE);
+ /* disable Audio L preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPLDCPRECHARGE_DISABLE);
+ 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);
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* R preamplifier enable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_ENABLE);
+ /* R ADC input sel : R PGA. Enable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRINPUTSEL_MASK,
+ MT6357_AUDADCRINPUTSEL_PREAMPLIFIER);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRUP);
+ /* Audio R preamplifier DCC precharge off */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Audio R ADC input sel : off, disable audio R ADC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDADCRPWRUP_MASK, MT6357_AUDADCRPWRDOWN);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDADCRINPUTSEL_MASK, MT6357_AUDADCRINPUTSEL_IDLE);
+ /* R preamplifier ACC */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCCEN_MASK, MT6357_AUDPREAMPRDCCEN_AC);
+ /* R preamplifier disable */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRON_MASK, MT6357_AUDPREAMPRON_DISABLE);
+ /* disable Audio R preamplifier DCC precharge */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRDCPRECHARGE_MASK,
+ MT6357_AUDPREAMPRDCPRECHARGE_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int adc_enable_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON0, &lgain);
+ regmap_read(priv->regmap, MT6357_AUDENC_ANA_CON1, &rgain);
+ /* L PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPLGAIN_SFT);
+ /* R PGA 0 dB gain */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK,
+ UL_GAIN_0DB << MT6357_AUDPREAMPRGAIN_SFT);
+ /* enable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE);
+ /* UL turn on */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_ENABLE);
+ /* Wait to avoid any pop noises */
+ msleep(100);
+ /* set the mic gains to the stored values */
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON0,
+ MT6357_AUDPREAMPLGAIN_MASK, lgain);
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON1,
+ MT6357_AUDPREAMPRGAIN_MASK, rgain);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* UL turn off */
+ regmap_update_bits(priv->regmap, MT6357_AFE_UL_SRC_CON0_L,
+ MT6357_UL_SRC_ON_TMP_CTL_MASK, MT6357_UL_SRC_DISABLE);
+ /* disable aud_pad TX fifos */
+ regmap_update_bits(priv->regmap, MT6357_AFE_AUD_PAD_TOP,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK,
+ MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void configure_downlinks(struct mt6357_priv *priv, bool enable)
+{
+ if (enable) {
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ELR_0,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK,
+ MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE);
+ /* Disable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK | MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* Disable lineout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* Reduce ESD resistance of AU_REFN */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_AUD_REFN_DERES_VAUDP15_MASK,
+ MT6357_AUD_REFN_DERES_VAUDP15_ENABLE);
+ /* Turn on DA_600K_NCP_VA18 */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON1, MT6357_DIVCKS_ON);
+ /* Set NCP clock as 604kHz // 26MHz/43 = 604KHz */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON2, 0x002c);
+ /* Toggle DIVCKS_CHG */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON0, MT6357_DIVCKS_CHG);
+ /* Set NCP soft start mode as default mode: 150us */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON4,
+ MT6357_DIVCKS_PWD_NCP_ST_150US);
+ /* Enable NCP */
+ regmap_write(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_ENABLE);
+ usleep_range(250, 270);
+ /* Enable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_ENABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_LCLDO_EN_VA18_ENABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE |
+ MT6357_HCLDO_EN_VA18_ENABLE);
+ /* Enable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK, MT6357_NVREG_EN_VAUDP15_ENABLE);
+ usleep_range(100, 120);
+ /* Enable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE);
+ /* Enable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_ENABLE);
+ /* Enable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_ENABLE);
+ usleep_range(100, 120);
+ } else {
+ /* Disable low-noise mode of DAC */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_DAC_LOW_NOISE_MODE_MASK,
+ MT6357_DAC_LOW_NOISE_MODE_DISABLE);
+ /* Disable AUD_CLK */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON11,
+ MT6357_RSTB_DECODER_VA28_MASK,
+ MT6357_RSTB_DECODER_VA28_DISABLE);
+ /* Enable linout short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_ENABLE);
+ /* Enable handset short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_ENABLE);
+ /* Enable headphone short-circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_ENABLE);
+ /* Disable IBIST */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON10,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK,
+ MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE);
+ /* Disable NV regulator (-1.2V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON13,
+ MT6357_NVREG_EN_VAUDP15_MASK,
+ MT6357_NVREG_EN_VAUDP15_DISABLE);
+ /* Disable cap-less LDOs (1.5V) */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON12,
+ MT6357_VA33REFGEN_EN_VA18_MASK |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_LCLDO_EN_VA18_MASK |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_MASK |
+ MT6357_HCLDO_EN_VA18_MASK,
+ MT6357_VA33REFGEN_EN_VA18_DISABLE |
+ MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_LCLDO_EN_VA18_DISABLE |
+ MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE |
+ MT6357_HCLDO_EN_VA18_DISABLE);
+ /* Disable NCP */
+ regmap_update_bits(priv->regmap, MT6357_AUDNCP_CLKDIV_CON3,
+ MT6357_DIVCKS_PWD_NCP_MASK, MT6357_DIVCKS_PWD_NCP_DISABLE);
+ }
+}
+
+static int mt_audio_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ set_playback_gpio(priv, true);
+
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+
+ /* Disable HP main CMFB Switch */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Audio system digital clock power down release */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_NORMAL_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ /* sdm audio fifo clock power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_ENABLE);
+ /* scrambler clock on enable */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+ /* sdm power on */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_ENABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_ON |
+ MT6357_CCI_ACD_FUNC_RSTB_RELEASE);
+
+ configure_downlinks(priv, true);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ configure_downlinks(priv, false);
+ /* DL scrambler disabling sequence */
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON2,
+ MT6357_CCI_AUDIO_FIFO_DISABLE |
+ MT6357_CCI_ACD_MODE_TEST_PATH |
+ MT6357_CCI_AFIFO_CLK_PWDB_DOWN |
+ MT6357_CCI_ACD_FUNC_RSTB_RESET);
+ regmap_write(priv->regmap, MT6357_AFUNC_AUD_CON0,
+ MT6357_CCI_AUD_ANACK_INVERT |
+ (4 << MT6357_CCI_AUDIO_FIFO_WPTR_SFT) |
+ MT6357_CCI_SCRAMBLER_CG_ENABLE |
+ MT6357_CCI_RAND_ENABLE |
+ MT6357_CCI_SPLT_SCRMB_CLK_ON |
+ MT6357_CCI_SPLT_SCRMB_ON |
+ MT6357_CCI_ZERO_PADDING_DISABLE |
+ MT6357_CCI_SCRAMBLER_DISABLE);
+
+ set_playback_gpio(priv, false);
+
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ 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;
+}
+
+static int lo_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON1, &lgain);
+ rgain = (lgain & MT6357_AUD_LOR_GAIN_MASK) >> MT6357_AUD_LOR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_LOL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE);
+ /* Set LOL gain to normal gain step by step */
+ lo_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease LOL gain to minimum gain step by step */
+
+ lo_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Disable LO driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE);
+ /* Disable LO driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE);
+ /* Clear LO STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON1,
+ MT6357_AUD_LOL_GAIN_MASK |
+ MT6357_AUD_LOR_GAIN_MASK,
+ lgain << MT6357_AUD_LOL_GAIN_SFT |
+ rgain << MT6357_AUD_LOR_GAIN_SFT);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hs_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int gain; /* HS register has only one gain slot */
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON3, &gain);
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK,
+ DL_GAIN_N_40DB);
+
+ /* Set HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE);
+ /* Enable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Enable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE);
+ /* Set HS gain to normal gain step by step */
+ hs_volume_ramp(priv, DL_GAIN_N_40DB, gain);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* decrease HS gain to minimum gain step by step */
+ hs_volume_ramp(priv, gain, DL_GAIN_N_40DB);
+ /* Disable HS driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE);
+ /* Disable HS driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE);
+ /* Clear HS STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON3,
+ MT6357_AUD_HS_GAIN_MASK, gain);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int hp_main_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+ int lgain, rgain;
+
+ /* Get current gain value */
+ regmap_read(priv->regmap, MT6357_ZCD_CON2, &lgain);
+ rgain = (lgain & MT6357_AUD_HPR_GAIN_MASK) >> MT6357_AUD_HPR_GAIN_SFT;
+ lgain = lgain & MT6357_AUD_HPL_GAIN_MASK;
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ priv->hp_channel_number++;
+ if (priv->hp_channel_number > 1)
+ break;
+ /* Set -40dB before enable HS to avoid POP noise */
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ MT6357_DL_GAIN_N_40DB_REG);
+ /* Set HPP/N STB enhance circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON2,
+ MT6357_HPROUT_STB_ENH_VAUDP15_MASK |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_MASK,
+ MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 |
+ MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_NORMAL |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Enable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE);
+ /* Enable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE);
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE);
+ /* Disable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Enable HP main output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE);
+ /* Enable HPR/L main output stage step by step */
+ hp_main_output_ramp(priv, true);
+ usleep_range(1000, 1200);
+ /* Reduce HP aux feedback loop gain */
+ hp_aux_feedback_loop_gain_ramp(priv, true);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* apply volume setting */
+ hp_volume_ramp(priv, DL_GAIN_N_40DB, lgain,
+ DL_GAIN_N_40DB, rgain);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ usleep_range(100, 120);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_channel_number--;
+ if (priv->hp_channel_number > 0)
+ break;
+ /* Short HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE);
+ /* Enable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE);
+ /* decrease HPL/R gain to normal gain step by step */
+ hp_volume_ramp(priv, lgain, DL_GAIN_N_40DB,
+ rgain, DL_GAIN_N_40DB);
+ /* Enable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE);
+ /* 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, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE);
+ /* Enable HP aux CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HP_CMFB_RST_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK,
+ MT6357_HP_CMFB_RST_RESET |
+ MT6357_HPL_AUX_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_ENABLE);
+ /* Disable HP main CMFB loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE);
+ /* Unshort HP main output to HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK,
+ MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE |
+ MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE);
+ /* Disable HP driver core circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_MASK |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE);
+ /* Disable HP driver bias circuits */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_BIAS_VAUDP15_MASK |
+ MT6357_AUD_HPL_BIAS_VAUDP15_MASK,
+ MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE);
+ /* Disable HP aux CMFB loop,
+ * Enable HP main CMFB for HP off state
+ */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON6,
+ MT6357_HPRL_MAIN_CMFB_LOOP_MASK |
+ MT6357_HPR_AUX_CMFB_LOOP_MASK |
+ MT6357_HPL_AUX_CMFB_LOOP_MASK,
+ MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE |
+ MT6357_HPR_AUX_CMFB_LOOP_DISABLE |
+ MT6357_HPL_AUX_CMFB_LOOP_DISABLE);
+ /* Disable HP aux feedback loop */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_MASK |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_MASK,
+ MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE |
+ MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE);
+ /* Disable HP aux output stage */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON1,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK,
+ MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE |
+ MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE);
+ /* Save the gain value into the register*/
+ regmap_update_bits(priv->regmap, MT6357_ZCD_CON2,
+ MT6357_AUD_HPL_GAIN_MASK |
+ MT6357_AUD_HPR_GAIN_MASK,
+ lgain << MT6357_AUD_HPL_GAIN_SFT |
+ rgain << MT6357_AUD_HPR_GAIN_SFT);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int right_dac_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACR_PWRUP_VA28_MASK |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACR_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int left_dac_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 mt6357_priv *priv = snd_soc_component_get_drvdata(cmpnt);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_ENABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* disable Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, false);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Pull-down HPL/R to AVSS28_AUD */
+ if (priv->pull_down_needed)
+ hp_pull_down(priv, true);
+ /* Disable Audio DAC and control audio bias gen */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_DACL_PWRUP_VA28_MASK |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_MASK,
+ MT6357_AUD_DACL_PWRUP_VA28_DISABLE |
+ MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Supply widgets subsequence */
+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,
+};
+
+/* DAPM Widgets */
+static const struct snd_soc_dapm_widget mt6357_dapm_widgets[] = {
+ /* Analog Clocks */
+ SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF,
+ MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB,
+ MT6357_AUDDEC_ANA_CON11,
+ MT6357_AUDGLB_PWRDN_VA28_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ,
+ MT6357_AUDENC_ANA_CON6,
+ MT6357_CLKSQ_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDNCP_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_ZCD13M_CK_PDN_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_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,
+ MT6357_AUD_TOP_CKPDN_CON0,
+ MT6357_AUDIF_CK_PDN_SFT, 1, NULL, 0),
+
+ /* Digital Clocks */
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_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,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_DAC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_ADC_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_I2S_DL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PWR_CLK_DIS_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_RESERVED_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_LPBK", SUPPLY_SEQ_AUD_TOP,
+ MT6357_AUDIO_TOP_CON0,
+ MT6357_PDN_LPBK_CTL_SFT, 1, NULL, 0),
+
+ /* General */
+ SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE,
+ MT6357_AFE_UL_DL_CON0,
+ MT6357_AFE_ON_SFT, 0, NULL, 0),
+
+ /* Uplinks */
+ SND_SOC_DAPM_AIF_OUT_E("AIF1TX", "MT6357 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),
+ SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, adc_enable_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("PGA L Mux", SND_SOC_NOPM, 0, 0,
+ &pga_left_mux_control,
+ mt_pga_left_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX_E("PGA R Mux", SND_SOC_NOPM, 0, 0,
+ &pga_right_mux_control,
+ mt_pga_right_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ 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_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_SUPPLY("MICBIAS0", MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_PWD_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_PWD_SFT, 0, NULL, 0),
+
+ /* UL inputs */
+ SND_SOC_DAPM_INPUT("AIN0"),
+ SND_SOC_DAPM_INPUT("AIN1"),
+ SND_SOC_DAPM_INPUT("AIN2"),
+ SND_SOC_DAPM_INPUT("LPBK"),
+ SND_SOC_DAPM_INPUT("SGEN UL"),
+
+ /* Downlinks */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "MT6357 Playback", 0,
+ SND_SOC_NOPM, 0, 0,
+ mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_INPUT("SGEN DL"),
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &dac_mux_control),
+
+ SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM, 0, 0, right_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM, 0, 0, left_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_SUPPLY("DL Digital Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL Analog Supply", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DL SRC", MT6357_AFE_DL_SRC2_CON0_L,
+ MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX_E("Line Out Source", SND_SOC_NOPM, 0, 0, &lo_mux_control,
+ lo_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Handset Source", SND_SOC_NOPM, 0, 0, &hs_mux_control,
+ hs_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Right Source", SND_SOC_NOPM, 0, 0, &hpr_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_MUX_E("Headphone Left Source", SND_SOC_NOPM, 0, 0, &hpl_mux_control,
+ hp_main_mux_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ /* DL outputs */
+ SND_SOC_DAPM_OUTPUT("Headphones"),
+ SND_SOC_DAPM_OUTPUT("Hansdet"),
+ SND_SOC_DAPM_OUTPUT("Line out"),
+
+ /* Sine generator */
+ SND_SOC_DAPM_SUPPLY("SGEN UL Enable",
+ MT6357_AFE_TOP_CON0, MT6357_UL_SINE_ON_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SGEN Enable",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_DAC_EN_CTL_SFT, 0, mt_audio_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("SGEN MUTE",
+ MT6357_AFE_SGEN_CFG0,
+ MT6357_SGEN_MUTE_SW_CTL_SFT, 1, NULL, 0)
+};
+
+static const struct snd_soc_dapm_route mt6357_dapm_routes[] = {
+ /* Capture */
+ {"AIF1TX", NULL, "Mic Type 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"},
+
+ {"Mic Type Mux", "ACC", "ADC"},
+ {"Mic Type Mux", "DCC", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_DIFF", "ADC"},
+ {"Mic Type Mux", "DCC_ECM_SINGLE", "ADC"},
+ {"Mic Type Mux", "DMIC", "AIN0"},
+ {"Mic Type Mux", "DMIC", "AIN2"},
+ {"Mic Type Mux", "Loopback", "LPBK"},
+ {"Mic Type Mux", "Sine Generator", "SGEN UL"},
+
+ {"SGEN UL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+ {"SGEN UL", NULL, "SGEN UL Enable"},
+ {"SGEN UL", NULL, "SGEN MUTE"},
+ {"SGEN UL", NULL, "SGEN Enable"},
+
+ {"ADC", NULL, "PGA L Mux"},
+ {"ADC", NULL, "PGA R Mux"},
+ {"ADC", NULL, "ADC Supply"},
+
+ {"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"},
+
+ {"AIN0", NULL, "MICBIAS0"},
+ {"AIN1", NULL, "MICBIAS1"},
+ {"AIN2", NULL, "MICBIAS0"},
+ {"LPBK", NULL, "AUDIO_TOP_LPBK"},
+
+ /* Playback */
+ {"DAC Mux", "Normal Path", "AIF_RX"},
+ {"DAC Mux", "Sine Generator", "SGEN DL"},
+
+ {"AIF_RX", NULL, "DL SRC"},
+
+ {"SGEN DL", NULL, "DL SRC"},
+ {"SGEN DL", NULL, "SGEN MUTE"},
+ {"SGEN DL", NULL, "SGEN Enable"},
+ {"SGEN DL", NULL, "DL Digital Supply"},
+ {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"},
+
+ {"DACL", NULL, "DAC Mux"},
+ {"DACR", NULL, "DAC Mux"},
+
+ {"DL Analog Supply", NULL, "CLK_BUF"},
+ {"DL Analog Supply", NULL, "AUDGLB"},
+ {"DL Analog Supply", NULL, "CLKSQ Audio"},
+ {"DL Analog Supply", NULL, "AUDNCP_CK"},
+ {"DL Analog Supply", NULL, "ZCD13M_CK"},
+ {"DL Analog Supply", NULL, "AUD_CK"},
+ {"DL Analog Supply", NULL, "AUDIF_CK"},
+
+ {"DL Digital Supply", NULL, "AUDIO_TOP_AFE_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_DAC_CTL"},
+ {"DL Digital Supply", NULL, "AUDIO_TOP_PWR_CLK"},
+ {"DL Digital Supply", NULL, "AFE_ON"},
+
+ {"DACR", NULL, "DL Digital Supply"},
+ {"DACR", NULL, "DL Analog Supply"},
+ {"DACL", NULL, "DL Digital Supply"},
+ {"DACL", NULL, "DL Analog Supply"},
+
+ {"Line Out Source", "DACR", "DACR"},
+ {"Line Out Source", "Playback", "DACL"},
+ {"Line Out Source", "Test mode", "DACL"},
+
+ {"Handset Source", "DACR", "DACR"},
+ {"Handset Source", "Playback", "DACL"},
+ {"Handset Source", "Test mode", "DACL"},
+
+ {"Headphone Right Source", "DAC", "DACR"},
+ {"Headphone Right Source", "Line Out", "Line Out Source"},
+ {"Headphone Right Source", "Handset", "Handset Source"},
+
+ {"Headphone Left Source", "DAC", "DACL"},
+ {"Headphone Left Source", "Line Out", "Line Out Source"},
+ {"Headphone Left Source", "Handset", "Handset Source"},
+
+ {"Line out", NULL, "Line Out Source"},
+ {"Hansdet", NULL, "Handset Source"},
+
+ {"Headphones", NULL, "Headphone Right Source"},
+ {"Headphones", NULL, "Headphone Left Source"},
+};
+
+static struct snd_soc_dai_driver mtk_6357_dai_codecs[] = {
+ {
+ .name = "mt6357-snd-codec-aif1",
+ .playback = {
+ .stream_name = "MT6357 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ .capture = {
+ .stream_name = "MT6357 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MT6357_SOC_HIGH_USE_RATE,
+ .formats = MT6357_SND_SOC_ADV_MT_FMTS,
+ },
+ },
+};
+
+static int mt6357_codec_probe(struct snd_soc_component *codec)
+{
+ struct mt6357_priv *priv = snd_soc_component_get_drvdata(codec);
+
+ snd_soc_component_init_regmap(codec, priv->regmap);
+
+ /* Enable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK, MT6357_XO_AUDIO_EN_M_ENABLE);
+ /* Disable HeadphoneL/HeadphoneR short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON0,
+ MT6357_AUD_HPR_SC_VAUDP15_MASK |
+ MT6357_AUD_HPL_SC_VAUDP15_MASK,
+ MT6357_AUD_HPR_SC_VAUDP15_DISABLE |
+ MT6357_AUD_HPL_SC_VAUDP15_DISABLE);
+ /* Disable voice short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON3,
+ MT6357_AUD_HS_SC_VAUDP15_MASK,
+ MT6357_AUD_HS_SC_VAUDP15_DISABLE);
+ /* disable LO buffer left short circuit protection */
+ regmap_update_bits(priv->regmap, MT6357_AUDDEC_ANA_CON4,
+ MT6357_AUD_LOL_SC_VAUDP15_MASK,
+ MT6357_AUD_LOL_SC_VAUDP15_DISABLE);
+ /* set gpio */
+ set_playback_gpio(priv, false);
+ set_capture_gpio(priv, false);
+ /* Disable audio part */
+ regmap_update_bits(priv->regmap, MT6357_DCXO_CW14,
+ MT6357_XO_AUDIO_EN_M_MASK,
+ MT6357_XO_AUDIO_EN_M_DISABLE);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver mt6357_soc_component_driver = {
+ .probe = mt6357_codec_probe,
+ .read = snd_soc_component_read,
+ .write = snd_soc_component_write,
+ .controls = mt6357_controls,
+ .num_controls = ARRAY_SIZE(mt6357_controls),
+ .dapm_widgets = mt6357_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt6357_dapm_widgets),
+ .dapm_routes = mt6357_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(mt6357_dapm_routes),
+};
+
+static const u32 micbias_values[] = {
+ 1700000, 1800000, 1900000, 2000000,
+ 2100000, 2500000, 2600000, 2700000
+};
+
+static u32 mt6357_get_micbias_idx(struct device_node *np, const char *micbias)
+{
+ int err;
+ u32 idx, val;
+
+ err = of_property_read_u32(np, micbias, &val);
+ if (err)
+ return 0;
+
+ for (idx = 0; idx < ARRAY_SIZE(micbias_values); idx++) {
+ if (val == micbias_values[idx])
+ return idx;
+ }
+ return 0;
+}
+
+static int mt6357_parse_dt(struct mt6357_priv *priv)
+{
+ u32 micbias_voltage_index = 0;
+ struct device_node *np = priv->dev->parent->of_node;
+
+ if (!np)
+ return -EINVAL;
+
+ priv->pull_down_needed = false;
+ if (of_property_read_bool(np, "mediatek,hp-pull-down"))
+ priv->pull_down_needed = true;
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias0-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON8,
+ MT6357_AUD_MICBIAS0_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS0_VREF_SFT);
+
+ micbias_voltage_index = mt6357_get_micbias_idx(np, "mediatek,micbias1-microvolt");
+ regmap_update_bits(priv->regmap, MT6357_AUDENC_ANA_CON9,
+ MT6357_AUD_MICBIAS1_VREF_MASK,
+ micbias_voltage_index << MT6357_AUD_MICBIAS1_VREF_SFT);
+
+ return 0;
+}
+
+static int mt6357_platform_driver_probe(struct platform_device *pdev)
+{
+ struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent);
+ struct mt6357_priv *priv;
+ int ret;
+
+ ret = devm_regulator_get_enable(&pdev->dev, "vaud28");
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to enable vaud28 regulator\n");
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*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);
+
+ ret = mt6357_parse_dt(priv);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to parse dts\n");
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64);
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+ return devm_snd_soc_register_component(&pdev->dev,
+ &mt6357_soc_component_driver,
+ mtk_6357_dai_codecs,
+ ARRAY_SIZE(mtk_6357_dai_codecs));
+}
+
+static const struct platform_device_id mt6357_platform_ids[] = {
+ {"mt6357-sound", 0},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(platform, mt6357_platform_ids);
+
+static struct platform_driver mt6357_platform_driver = {
+ .driver = {
+ .name = "mt6357-sound",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = mt6357_platform_driver_probe,
+ .id_table = mt6357_platform_ids,
+};
+
+module_platform_driver(mt6357_platform_driver)
+
+MODULE_DESCRIPTION("MT6357 ALSA SoC codec driver");
+MODULE_AUTHOR("Nicolas Belin <nbelin@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/mt6357.h b/sound/soc/codecs/mt6357.h
new file mode 100644
index 000000000000..7f6fccada6a2
--- /dev/null
+++ b/sound/soc/codecs/mt6357.h
@@ -0,0 +1,660 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6357.h -- mt6357 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2024 Baylibre
+ * Author: Nicolas Belin <nbelin@baylibre.com>
+ */
+
+#ifndef __MT6357_H__
+#define __MT6357_H__
+
+#include <linux/types.h>
+
+/* Reg bit defines */
+/* MT6357_GPIO_DIR0 */
+#define MT6357_GPIO8_DIR_MASK BIT(8)
+#define MT6357_GPIO8_DIR_INPUT 0
+#define MT6357_GPIO8_DIR_OUTPUT BIT(8)
+#define MT6357_GPIO9_DIR_MASK BIT(9)
+#define MT6357_GPIO9_DIR_INPUT 0
+#define MT6357_GPIO9_DIR_OUTPUT BIT(9)
+#define MT6357_GPIO10_DIR_MASK BIT(10)
+#define MT6357_GPIO10_DIR_INPUT 0
+#define MT6357_GPIO10_DIR_OUTPUT BIT(10)
+#define MT6357_GPIO11_DIR_MASK BIT(11)
+#define MT6357_GPIO11_DIR_INPUT 0
+#define MT6357_GPIO11_DIR_OUTPUT BIT(11)
+#define MT6357_GPIO12_DIR_MASK BIT(12)
+#define MT6357_GPIO12_DIR_INPUT 0
+#define MT6357_GPIO12_DIR_OUTPUT BIT(12)
+#define MT6357_GPIO13_DIR_MASK BIT(13)
+#define MT6357_GPIO13_DIR_INPUT 0
+#define MT6357_GPIO13_DIR_OUTPUT BIT(13)
+#define MT6357_GPIO14_DIR_MASK BIT(14)
+#define MT6357_GPIO14_DIR_INPUT 0
+#define MT6357_GPIO14_DIR_OUTPUT BIT(14)
+#define MT6357_GPIO15_DIR_MASK BIT(15)
+#define MT6357_GPIO15_DIR_INPUT 0
+#define MT6357_GPIO15_DIR_OUTPUT BIT(15)
+
+/* MT6357_GPIO_MODE2 */
+#define MT6357_GPIO8_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO8_MODE_GPIO 0
+#define MT6357_GPIO9_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO9_MODE_GPIO 0
+#define MT6357_GPIO10_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO10_MODE_GPIO 0
+#define MT6357_GPIO11_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_AUD_SYNC_MOSI BIT(9)
+#define MT6357_GPIO11_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE2_SET */
+#define MT6357_GPIO8_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO8_MODE_SET_AUD_CLK_MOSI BIT(0)
+#define MT6357_GPIO9_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO9_MODE_SET_AUD_DAT_MOSI0 BIT(3)
+#define MT6357_GPIO10_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO10_MODE_SET_AUD_DAT_MOSI1 BIT(6)
+#define MT6357_GPIO11_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO11_MODE_SET_AUD_SYNC_MOSI BIT(9)
+
+/* MT6357_GPIO_MODE2_CLR */
+#define MT6357_GPIO_MODE2_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_GPIO_MODE3 */
+#define MT6357_GPIO12_MODE_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO12_MODE_GPIO 0
+#define MT6357_GPIO13_MODE_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO13_MODE_GPIO 0
+#define MT6357_GPIO14_MODE_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO14_MODE_GPIO 0
+#define MT6357_GPIO15_MODE_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_AUD_SYNC_MISO BIT(9)
+#define MT6357_GPIO15_MODE_GPIO 0
+
+/* MT6357_GPIO_MODE3_SET */
+#define MT6357_GPIO12_MODE_SET_MASK GENMASK(2, 0)
+#define MT6357_GPIO12_MODE_SET_AUD_CLK_MISO BIT(0)
+#define MT6357_GPIO13_MODE_SET_MASK GENMASK(5, 3)
+#define MT6357_GPIO13_MODE_SET_AUD_DAT_MISO0 BIT(3)
+#define MT6357_GPIO14_MODE_SET_MASK GENMASK(8, 6)
+#define MT6357_GPIO14_MODE_SET_AUD_DAT_MISO1 BIT(6)
+#define MT6357_GPIO15_MODE_SET_MASK GENMASK(11, 9)
+#define MT6357_GPIO15_MODE_SET_AUD_SYNC_MISO BIT(9)
+
+/* MT6357_GPIO_MODE3_CLR */
+#define MT6357_GPIO_MODE3_CLEAR_ALL GENMASK(15, 0)
+
+/* MT6357_DCXO_CW14 */
+#define MT6357_XO_AUDIO_EN_M_SFT 13
+#define MT6357_XO_AUDIO_EN_M_MASK BIT(13)
+#define MT6357_XO_AUDIO_EN_M_ENABLE BIT(13)
+#define MT6357_XO_AUDIO_EN_M_DISABLE 0
+
+/* MT6357_AUD_TOP_CKPDN_CON0 */
+#define MT6357_AUDNCP_CK_PDN_SFT 6
+#define MT6357_ZCD13M_CK_PDN_SFT 5
+#define MT6357_AUDIF_CK_PDN_SFT 2
+#define MT6357_AUD_CK_PDN_SFT 1
+
+/* MT6357_AUDNCP_CLKDIV_CON0 */
+#define MT6357_DIVCKS_CHG BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON1 */
+#define MT6357_DIVCKS_ON BIT(0)
+
+/* MT6357_AUDNCP_CLKDIV_CON3 */
+#define MT6357_DIVCKS_PWD_NCP_MASK BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_DISABLE BIT(0)
+#define MT6357_DIVCKS_PWD_NCP_ENABLE 0
+
+/* MT6357_AUDNCP_CLKDIV_CON4 */
+#define MT6357_DIVCKS_PWD_NCP_ST_SEL_MASK GENMASK(1, 0)
+#define MT6357_DIVCKS_PWD_NCP_ST_50US 0
+#define MT6357_DIVCKS_PWD_NCP_ST_100US 1
+#define MT6357_DIVCKS_PWD_NCP_ST_150US 2
+#define MT6357_DIVCKS_PWD_NCP_ST_200US 3
+
+/* MT6357_AFE_UL_DL_CON0 */
+#define MT6357_AFE_UL_LR_SWAP_SFT 15
+#define MT6357_AFE_ON_SFT 0
+
+/* MT6357_AFE_DL_SRC2_CON0_L */
+#define MT6357_DL_2_SRC_ON_TMP_CTL_PRE_SFT 0
+
+/* MT6357_AFE_UL_SRC_CON0_H */
+#define MT6357_C_TWO_DIGITAL_MIC_CTL_MASK BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_ENABLE BIT(7)
+#define MT6357_C_TWO_DIGITAL_MIC_DISABLE 0
+
+/* MT6357_AFE_UL_SRC_CON0_L */
+#define MT6357_UL_SDM_3_LEVEL_CTL_MASK BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_SELECT BIT(1)
+#define MT6357_UL_SDM_3_LEVEL_DESELECT 0
+#define MT6357_UL_SRC_ON_TMP_CTL_MASK BIT(0)
+#define MT6357_UL_SRC_ENABLE BIT(0)
+#define MT6357_UL_SRC_DISABLE 0
+
+/* MT6357_AFE_TOP_CON0 */
+#define MT6357_UL_SINE_ON_SFT 1
+#define MT6357_UL_SINE_ON_MASK BIT(1)
+#define MT6357_DL_SINE_ON_SFT 0
+#define MT6357_DL_SINE_ON_MASK BIT(0)
+
+/* MT6357_AUDIO_TOP_CON0 */
+#define MT6357_PDN_LPBK_CTL_SFT 15
+#define MT6357_PDN_AFE_CTL_SFT 7
+#define MT6357_PDN_DAC_CTL_SFT 6
+#define MT6357_PDN_ADC_CTL_SFT 5
+#define MT6357_PDN_I2S_DL_CTL_SFT 3
+#define MT6357_PWR_CLK_DIS_CTL_SFT 2
+#define MT6357_PDN_AFE_TESTMODEL_CTL_SFT 1
+#define MT6357_PDN_RESERVED_SFT 0
+
+/* MT6357_AFUNC_AUD_CON0 */
+#define MT6357_CCI_AUD_ANACK_INVERT BIT(15)
+#define MT6357_CCI_AUD_ANACK_NORMAL 0
+#define MT6357_CCI_AUDIO_FIFO_WPTR_SFT 12
+#define MT6357_CCI_SCRAMBLER_CG_ENABLE BIT(11)
+#define MT6357_CCI_SCRAMBLER_CG_DISABLE 0
+#define MT6357_CCI_LCK_INV_OUT_OF_PHASE BIT(10)
+#define MT6357_CCI_LCK_INV_IN_PHASE 0
+#define MT6357_CCI_RAND_ENABLE BIT(9)
+#define MT6357_CCI_RAND_DISABLE 0
+#define MT6357_CCI_SPLT_SCRMB_CLK_ON BIT(8)
+#define MT6357_CCI_SPLT_SCRMB_CLK_OFF 0
+#define MT6357_CCI_SPLT_SCRMB_ON BIT(7)
+#define MT6357_CCI_SPLT_SCRMB_OFF 0
+#define MT6357_CCI_AUD_IDAC_TEST_EN_FROM_TEST_IN BIT(6)
+#define MT6357_CCI_AUD_IDAC_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_ZERO_PADDING_DISABLE BIT(5)
+#define MT6357_CCI_ZERO_PADDING_ENABLE 0
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_FROM_TEST_IN BIT(4)
+#define MT6357_CCI_AUD_SPLIT_TEST_EN_NORMAL_PATH 0
+#define MT6357_CCI_AUD_SDM_MUTE_L_REG_CTL BIT(3)
+#define MT6357_CCI_AUD_SDM_MUTE_L_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_MUTE_R_REG_CTL BIT(2)
+#define MT6357_CCI_AUD_SDM_MUTE_R_NO_CTL 0
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER3 BIT(1)
+#define MT6357_CCI_AUD_SDM_7BIT_FROM_SPLITTER1 0
+#define MT6357_CCI_SCRAMBLER_ENABLE BIT(0)
+#define MT6357_CCI_SCRAMBLER_DISABLE 0
+
+/* MT6357_AFUNC_AUD_CON2 */
+#define MT6357_CCI_AUDIO_FIFO_ENABLE BIT(3)
+#define MT6357_CCI_AUDIO_FIFO_DISABLE 0
+#define MT6357_CCI_ACD_MODE_NORMAL_PATH BIT(2)
+#define MT6357_CCI_ACD_MODE_TEST_PATH 0
+#define MT6357_CCI_AFIFO_CLK_PWDB_ON BIT(1)
+#define MT6357_CCI_AFIFO_CLK_PWDB_DOWN 0
+#define MT6357_CCI_ACD_FUNC_RSTB_RELEASE BIT(0)
+#define MT6357_CCI_ACD_FUNC_RSTB_RESET 0
+
+/* MT6357_AFE_ADDA_MTKAIF_CFG0 */
+#define MT6357_ADDA_MTKAIF_LPBK_CTL_MASK BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_ENABLE BIT(1)
+#define MT6357_ADDA_MTKAIF_LPBK_DISABLE 0
+
+/* MT6357_AFE_SGEN_CFG0 */
+#define MT6357_SGEN_DAC_EN_CTL_SFT 7
+#define MT6357_SGEN_DAC_ENABLE BIT(7)
+#define MT6357_SGEN_MUTE_SW_CTL_SFT 6
+#define MT6357_SGEN_MUTE_SW_DISABLE 0
+
+/* MT6357_AFE_DCCLK_CFG0 */
+#define MT6357_DCCLK_DIV_MASK GENMASK(15, 5)
+#define MT6357_DCCLK_DIV_SFT 5
+#define MT6357_DCCLK_DIV_RUN_VALUE (32 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_DIV_STOP_VALUE (259 << MT6357_DCCLK_DIV_SFT)
+#define MT6357_DCCLK_PDN_MASK BIT(1)
+#define MT6357_DCCLK_PDN BIT(1)
+#define MT6357_DCCLK_OUTPUT 0
+#define MT6357_DCCLK_GEN_ON_MASK BIT(0)
+#define MT6357_DCCLK_GEN_ON BIT(0)
+#define MT6357_DCCLK_GEN_OFF 0
+
+/* MT6357_AFE_DCCLK_CFG1 */
+#define MT6357_DCCLK_RESYNC_BYPASS_MASK BIT(8)
+#define MT6357_DCCLK_RESYNC_BYPASS BIT(8)
+
+/* MT6357_AFE_AUD_PAD_TOP */
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_MASK GENMASK(15, 8)
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_ENABLE (BIT(13) | BIT(12) | BIT(8))
+#define MT6357_AUD_PAD_TX_FIFO_NORMAL_PATH_DISABLE (BIT(13) | BIT(12))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_MASK GENMASK(7, 0)
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_ENABLE (BIT(5) | BIT(4) | BIT(0))
+#define MT6357_AUD_PAD_TX_FIFO_LPBK_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON0 */
+#define MT6357_AUDADCLINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCLINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCLINPUTSEL_IDLE 0
+#define MT6357_AUDADCLPWRUP_SFT 12
+#define MT6357_AUDADCLPWRUP_MASK BIT(12)
+#define MT6357_AUDADCLPWRUP BIT(12)
+#define MT6357_AUDADCLPWRDOWN 0
+#define MT6357_AUDPREAMPLGAIN_SFT 8
+#define MT6357_AUDPREAMPLGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPLGAIN_MAX 4
+#define MT6357_AUDPREAMPLINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPLINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPLDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPLDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPLDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPLDCCEN_AC 0
+#define MT6357_AUDPREAMPLON_MASK BIT(0)
+#define MT6357_AUDPREAMPLON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPLON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON1 */
+#define MT6357_AUDADCRINPUTSEL_MASK GENMASK(14, 13)
+#define MT6357_AUDADCRINPUTSEL_PREAMPLIFIER BIT(14)
+#define MT6357_AUDADCRINPUTSEL_IDLE 0
+#define MT6357_AUDADCRPWRUP_SFT 12
+#define MT6357_AUDADCRPWRUP_MASK BIT(12)
+#define MT6357_AUDADCRPWRUP BIT(12)
+#define MT6357_AUDADCRPWRDOWN 0
+#define MT6357_AUDPREAMPRGAIN_SFT 8
+#define MT6357_AUDPREAMPRGAIN_MASK GENMASK(10, 8)
+#define MT6357_AUDPREAMPRGAIN_MAX 4
+#define MT6357_AUDPREAMPRINPUTSEL_SFT 6
+#define MT6357_AUDPREAMPRINPUTSEL_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUDPREAMPRDCPRECHARGE_MASK BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_ENABLE BIT(2)
+#define MT6357_AUDPREAMPRDCPRECHARGE_DISABLE 0
+#define MT6357_AUDPREAMPRDCCEN_MASK BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_DC BIT(1)
+#define MT6357_AUDPREAMPRDCCEN_AC 0
+#define MT6357_AUDPREAMPRON_MASK BIT(0)
+#define MT6357_AUDPREAMPRON_ENABLE BIT(0)
+#define MT6357_AUDPREAMPRON_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON6 */
+#define MT6357_CLKSQ_EN_SFT 0
+
+/* MT6357_AUDENC_ANA_CON7 */
+#define MT6357_AUDDIGMICBIAS_MASK GENMASK(2, 1)
+#define MT6357_AUDDIGMICBIAS_DEFAULT_VALUE BIT(2)
+#define MT6357_AUDDIGMICBIAS_OFF 0
+#define MT6357_AUDDIGMICEN_MASK BIT(0)
+#define MT6357_AUDDIGMICEN_ENABLE BIT(0)
+#define MT6357_AUDDIGMICEN_DISABLE 0
+
+/* MT6357_AUDENC_ANA_CON8 */
+#define MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_ENABLE BIT(14)
+#define MT6357_AUD_MICBIAS0_DCSW2N_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE BIT(13)
+#define MT6357_AUD_MICBIAS0_DCSW2P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE BIT(12)
+#define MT6357_AUD_MICBIAS0_DCSW2P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSW0N_ENABLE BIT(10)
+#define MT6357_AUD_MICBIAS0_DCSWN_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE BIT(9)
+#define MT6357_AUD_MICBIAS0_DCSW0P2_DISABLE 0
+#define MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS0_DCSW0P1_DISABLE 0
+#define MT6357_AUD_MICBIAS0_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS0_VREF_SFT 4
+#define MT6357_AUD_MICBIAS0_PWD_SFT 0
+
+#define MT6357_AUD_MICBIAS0_DC_MASK (MT6357_AUD_MICBIAS0_DCSW2N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0N_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_EN_MASK | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_EN_MASK)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_ALL (MT6357_AUD_MICBIAS0_DCSW2N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0N_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P2_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_ENABLE_P1 (MT6357_AUD_MICBIAS0_DCSW2P1_ENABLE | \
+ MT6357_AUD_MICBIAS0_DCSW0P1_ENABLE)
+
+#define MT6357_AUD_MICBIAS0_DC_DISABLE_ALL 0
+
+/* MT6357_AUDENC_ANA_CON9 */
+#define MT6357_AUD_MICBIAS1_DCSW1P_EN_MASK BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_ENABLE BIT(8)
+#define MT6357_AUD_MICBIAS1_DCSW1P_DISABLE 0
+#define MT6357_AUD_MICBIAS1_VREF_MASK GENMASK(6, 4)
+#define MT6357_AUD_MICBIAS1_VREF_SFT 4
+#define MT6357_AUD_MICBIAS1_PWD_SFT 0
+
+/* MT6357_AUDDEC_ANA_CON0 */
+#define MT6357_AUD_HPR_SC_VAUDP15_MASK BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_DISABLE BIT(13)
+#define MT6357_AUD_HPR_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPL_SC_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_DISABLE BIT(12)
+#define MT6357_AUD_HPL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPR_MUX_INPUT_VAUDP15_SFT 10
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HPL_MUX_INPUT_VAUDP15_SFT 8
+#define MT6357_AUD_HPR_BIAS_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HPR_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_BIAS_VAUDP15_MASK BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_ENABLE BIT(6)
+#define MT6357_AUD_HPL_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_MASK BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_ENABLE BIT(5)
+#define MT6357_AUD_HPR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_ENABLE BIT(4)
+#define MT6357_AUD_HPL_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VA28_MASK BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_ENABLE BIT(3)
+#define MT6357_AUD_DACL_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VA28_MASK BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_ENABLE BIT(2)
+#define MT6357_AUD_DACR_PWRUP_VA28_DISABLE 0
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_DACR_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_DACL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON1 */
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_MASK GENMASK(14, 12)
+#define MT6357_HPROUT_STG_CTRL_VAUDP15_SFT 12
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MASK GENMASK(10, 8)
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_SFT 8
+#define MT6357_HPLOUT_STG_CTRL_VAUDP15_MAX 7
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_MASK BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(7)
+#define MT6357_HPR_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_MASK BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_ENABLE BIT(6)
+#define MT6357_HPL_SHORT2HPR_AUX_VAUDP15_DISABLE 0
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_MASK BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_ENABLE BIT(5)
+#define MT6357_HPR_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_MASK BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_ENABLE BIT(4)
+#define MT6357_HPL_AUX_FBRSW_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_MASK BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_ENABLE BIT(3)
+#define MT6357_HPROUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_MASK BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_ENABLE BIT(2)
+#define MT6357_HPLOUT_AUX_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_PWRUP_VAUDP15_MASK BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_ENABLE BIT(1)
+#define MT6357_HPROUT_PWRUP_VAUDP15_DISABLE 0
+#define MT6357_HPLOUT_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_HPLOUT_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON2 */
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_MASK BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_ENABLE BIT(10)
+#define MT6357_HPP_SHORT_2VCM_VAUDP15_DISABLE 0
+#define MT6357_AUD_REFN_DERES_VAUDP15_MASK BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_ENABLE BIT(9)
+#define MT6357_AUD_REFN_DERES_VAUDP15_DISABLE 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_MASK GENMASK(6, 4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(4)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_POPEN BIT(5)
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P250 (BIT(4) | BIT(5))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(4) | BIT(6))
+#define MT6357_HPROUT_STB_ENH_VAUDP15_N470_P470 (BIT(4) | BIT(5) | BIT(6))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_MASK GENMASK(2, 0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_OPEN 0
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P250 BIT(0)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_POPEN BIT(1)
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P250 (BIT(0) | BIT(1))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_NOPEN_P470 (BIT(0) | BIT(2))
+#define MT6357_HPLOUT_STB_ENH_VAUDP15_N470_P470 (BIT(0) | BIT(1) | BIT(2))
+
+/* MT6357_AUDDEC_ANA_CON3 */
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_MASK BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_ENABLE BIT(7)
+#define MT6357_AUD_HSOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_HS_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_HS_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_HS_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_HS_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_HS_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON4 */
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_ENABLE BIT(8)
+#define MT6357_AUD_LOLOUT_STB_ENH_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_SC_VAUDP15_MASK BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_DISABLE BIT(4)
+#define MT6357_AUD_LOL_SC_VAUDP15_ENABLE 0
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_MASK_NOSFT GENMASK(1, 0)
+#define MT6357_AUD_LOL_MUX_INPUT_VAUDP15_SFT 2
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_MASK BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_ENABLE BIT(1)
+#define MT6357_AUD_LOL_PWRUP_BIAS_VAUDP15_DISABLE 0
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_MASK BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_ENABLE BIT(0)
+#define MT6357_AUD_LOL_PWRUP_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON6 */
+#define MT6357_HP_AUX_LOOP_GAIN_MASK GENMASK(15, 12)
+#define MT6357_HP_AUX_LOOP_GAIN_SFT 12
+#define MT6357_HP_AUX_LOOP_GAIN_MAX 0x0f
+#define MT6357_HPR_AUX_CMFB_LOOP_MASK BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_ENABLE BIT(11)
+#define MT6357_HPR_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPL_AUX_CMFB_LOOP_MASK BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_ENABLE BIT(10)
+#define MT6357_HPL_AUX_CMFB_LOOP_DISABLE 0
+#define MT6357_HPRL_MAIN_CMFB_LOOP_MASK BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_ENABLE BIT(9)
+#define MT6357_HPRL_MAIN_CMFB_LOOP_DISABLE 0
+#define MT6357_HP_CMFB_RST_MASK BIT(7)
+#define MT6357_HP_CMFB_RST_NORMAL BIT(7)
+#define MT6357_HP_CMFB_RST_RESET 0
+#define MT6357_DAC_LOW_NOISE_MODE_MASK BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_ENABLE BIT(0)
+#define MT6357_DAC_LOW_NOISE_MODE_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON7 */
+#define MT6357_HP_IVBUF_DEGAIN_SFT 2
+#define MT6357_HP_IVBUF_DEGAIN_MAX 1
+
+/* MT6357_AUDDEC_ANA_CON10 */
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_MASK BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_DISABLE BIT(8)
+#define MT6357_AUD_IBIAS_PWRDN_VAUDP15_ENABLE 0
+
+/* MT6357_AUDDEC_ANA_CON11 */
+#define MT6357_RSTB_ENCODER_VA28_MASK BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_ENABLE BIT(5)
+#define MT6357_RSTB_ENCODER_VA28_DISABLE 0
+#define MT6357_AUDGLB_PWRDN_VA28_SFT 4
+#define MT6357_RSTB_DECODER_VA28_MASK BIT(0)
+#define MT6357_RSTB_DECODER_VA28_ENABLE BIT(0)
+#define MT6357_RSTB_DECODER_VA28_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON12 */
+#define MT6357_VA28REFGEN_EN_VA28_MASK BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_ENABLE BIT(13)
+#define MT6357_VA28REFGEN_EN_VA28_DISABLE 0
+#define MT6357_VA33REFGEN_EN_VA18_MASK BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_ENABLE BIT(12)
+#define MT6357_VA33REFGEN_EN_VA18_DISABLE 0
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_MASK BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_ENABLE BIT(10)
+#define MT6357_LCLDO_ENC_REMOTE_SENSE_VA28_DISABLE 0
+#define MT6357_LCLDO_ENC_EN_VA28_MASK BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_ENABLE BIT(8)
+#define MT6357_LCLDO_ENC_EN_VA28_DISABLE 0
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_MASK BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_ENABLE BIT(6)
+#define MT6357_LCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_LCLDO_EN_VA18_MASK BIT(4)
+#define MT6357_LCLDO_EN_VA18_ENABLE BIT(4)
+#define MT6357_LCLDO_EN_VA18_DISABLE 0
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_MASK BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_ENABLE BIT(2)
+#define MT6357_HCLDO_REMOTE_SENSE_VA18_DISABLE 0
+#define MT6357_HCLDO_EN_VA18_MASK BIT(0)
+#define MT6357_HCLDO_EN_VA18_ENABLE BIT(0)
+#define MT6357_HCLDO_EN_VA18_DISABLE 0
+
+/* MT6357_AUDDEC_ANA_CON13 */
+#define MT6357_NVREG_EN_VAUDP15_MASK BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_ENABLE BIT(0)
+#define MT6357_NVREG_EN_VAUDP15_DISABLE 0
+
+/* MT6357_AUDDEC_ELR_0 */
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_MASK BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_ENABLE BIT(12)
+#define MT6357_AUD_HP_TRIM_EN_VAUDP15_DISABLE 0
+
+/* MT6357_ZCD_CON1 */
+#define MT6357_AUD_LOL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_LOL_GAIN_SFT 0
+#define MT6357_AUD_LOR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_LOR_GAIN_SFT 7
+#define MT6357_AUD_LO_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON2 */
+#define MT6357_AUD_HPL_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HPL_GAIN_SFT 0
+#define MT6357_AUD_HPR_GAIN_MASK GENMASK(11, 7)
+#define MT6357_AUD_HPR_GAIN_SFT 7
+#define MT6357_AUD_HP_GAIN_MAX 0x12
+
+/* MT6357_ZCD_CON3 */
+#define MT6357_AUD_HS_GAIN_MASK GENMASK(4, 0)
+#define MT6357_AUD_HS_GAIN_SFT 0
+#define MT6357_AUD_HS_GAIN_MAX 0x12
+
+/* Registers list */
+/* gpio direction */
+#define MT6357_GPIO_DIR0 0x0088
+/* mosi */
+#define MT6357_GPIO_MODE2 0x00B6
+#define MT6357_GPIO_MODE2_SET 0x00B8
+#define MT6357_GPIO_MODE2_CLR 0x00BA
+/* miso */
+#define MT6357_GPIO_MODE3 0x00BC
+#define MT6357_GPIO_MODE3_SET 0x00BE
+#define MT6357_GPIO_MODE3_CLR 0x00C0
+
+#define MT6357_DCXO_CW14 0x07AC
+
+#define MT6357_AUD_TOP_CKPDN_CON0 0x208C
+#define MT6357_AUDNCP_CLKDIV_CON0 0x20B4
+#define MT6357_AUDNCP_CLKDIV_CON1 0x20B6
+#define MT6357_AUDNCP_CLKDIV_CON2 0x20B8
+#define MT6357_AUDNCP_CLKDIV_CON3 0x20BA
+#define MT6357_AUDNCP_CLKDIV_CON4 0x20BC
+#define MT6357_AFE_UL_DL_CON0 0x2108
+#define MT6357_AFE_DL_SRC2_CON0_L 0x210A
+#define MT6357_AFE_UL_SRC_CON0_H 0x210C
+#define MT6357_AFE_UL_SRC_CON0_L 0x210E
+#define MT6357_AFE_TOP_CON0 0x2110
+#define MT6357_AUDIO_TOP_CON0 0x2112
+#define MT6357_AFUNC_AUD_CON0 0x2116
+#define MT6357_AFUNC_AUD_CON2 0x211A
+#define MT6357_AFE_ADDA_MTKAIF_CFG0 0x2134
+#define MT6357_AFE_SGEN_CFG0 0x2140
+#define MT6357_AFE_DCCLK_CFG0 0x2146
+#define MT6357_AFE_DCCLK_CFG1 0x2148
+#define MT6357_AFE_AUD_PAD_TOP 0x214C
+#define MT6357_AUDENC_ANA_CON0 0x2188
+#define MT6357_AUDENC_ANA_CON1 0x218A
+#define MT6357_AUDENC_ANA_CON6 0x2194
+#define MT6357_AUDENC_ANA_CON7 0x2196
+#define MT6357_AUDENC_ANA_CON8 0x2198
+#define MT6357_AUDENC_ANA_CON9 0x219A
+#define MT6357_AUDDEC_ANA_CON0 0x2208
+#define MT6357_AUDDEC_ANA_CON1 0x220A
+#define MT6357_AUDDEC_ANA_CON2 0x220C
+#define MT6357_AUDDEC_ANA_CON3 0x220E
+#define MT6357_AUDDEC_ANA_CON4 0x2210
+#define MT6357_AUDDEC_ANA_CON6 0x2214
+#define MT6357_AUDDEC_ANA_CON7 0x2216
+#define MT6357_AUDDEC_ANA_CON10 0x221C
+#define MT6357_AUDDEC_ANA_CON11 0x221E
+#define MT6357_AUDDEC_ANA_CON12 0x2220
+#define MT6357_AUDDEC_ANA_CON13 0x2222
+#define MT6357_AUDDEC_ELR_0 0x2226
+#define MT6357_ZCD_CON1 0x228A
+#define MT6357_ZCD_CON2 0x228C
+#define MT6357_ZCD_CON3 0x228E
+
+enum {
+ DL_GAIN_8DB = 0,
+ DL_GAIN_0DB = 8,
+ DL_GAIN_N_1DB = 9,
+ DL_GAIN_N_10DB = 18,
+ DL_GAIN_N_12DB = 20,
+ DL_GAIN_N_40DB = 0x1f,
+};
+
+enum {
+ UL_GAIN_0DB = 0,
+ UL_GAIN_6DB,
+ UL_GAIN_12DB,
+ UL_GAIN_18DB,
+ UL_GAIN_24DB,
+};
+
+#define MT6357_DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB)
+#define MT6357_DL_GAIN_REG_LEFT_MASK 0x001f
+#define MT6357_DL_GAIN_REG_LEFT_SHIFT 0
+#define MT6357_DL_GAIN_REG_RIGHT_MASK 0x0f80
+#define MT6357_DL_GAIN_REG_RIGHT_SHIFT 7
+#define MT6357_DL_GAIN_REG_MASK 0x0f9f
+
+#define MT6357_SND_SOC_ADV_MT_FMTS (\
+ SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE |\
+ SNDRV_PCM_FMTBIT_U16_BE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_BE |\
+ SNDRV_PCM_FMTBIT_U24_LE |\
+ SNDRV_PCM_FMTBIT_U24_BE |\
+ SNDRV_PCM_FMTBIT_S32_LE |\
+ SNDRV_PCM_FMTBIT_S32_BE |\
+ SNDRV_PCM_FMTBIT_U32_LE |\
+ SNDRV_PCM_FMTBIT_U32_BE)
+
+#define MT6357_SOC_HIGH_USE_RATE (\
+ SNDRV_PCM_RATE_CONTINUOUS |\
+ SNDRV_PCM_RATE_8000_192000)
+
+/* codec private structure */
+struct mt6357_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ bool pull_down_needed;
+ int hp_channel_number;
+};
+#endif
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
new file mode 100644
index 000000000000..e033027fd4c7
--- /dev/null
+++ b/sound/soc/codecs/mt6358.c
@@ -0,0 +1,2431 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6358.c -- mt6358 ALSA SoC audio codec driver
+//
+// Copyright (c) 2018 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/platform_device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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;
+
+ 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);
+}
+
+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;
+}
+
+/* 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] =
+ (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK;
+ regmap_read(priv->regmap, MT6358_AUDENC_ANA_CON1, &reg);
+ 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 int mt6358_dmic_mode_get(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->dmic_one_wire_mode;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 0;
+}
+
+static int mt6358_dmic_mode_set(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->dmic_one_wire_mode != enabled) {
+ priv->dmic_one_wire_mode = enabled;
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ return 1;
+ }
+ dev_dbg(priv->dev, "%s() dmic_mode = %d", __func__, priv->dmic_one_wire_mode);
+
+ 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),
+
+ SOC_SINGLE_BOOL_EXT("Dmic Mode Switch", 0,
+ mt6358_dmic_mode_get, mt6358_dmic_mode_set),
+};
+
+/* 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 <kaichieh.chuang@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h
new file mode 100644
index 000000000000..b729c3899b7e
--- /dev/null
+++ b/sound/soc/codecs/mt6358.h
@@ -0,0 +1,2310 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * mt6358.h -- mt6358 ALSA SoC audio codec driver
+ *
+ * Copyright (c) 2018 MediaTek Inc.
+ * Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+ */
+
+#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);
+#endif /* __MT6358_H__ */
diff --git a/sound/soc/codecs/mt6359-accdet.c b/sound/soc/codecs/mt6359-accdet.c
new file mode 100644
index 000000000000..ed34cc15b80e
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.c
@@ -0,0 +1,1062 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359-accdet.c -- ALSA SoC mt6359 accdet driver
+//
+// Copyright (C) 2021 MediaTek Inc.
+// Author: Argus Lin <argus.lin@mediatek.com>
+//
+
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/kthread.h>
+#include <linux/io.h>
+#include <linux/sched/clock.h>
+#include <linux/workqueue.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <linux/mfd/mt6397/core.h>
+
+#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, &reg);
+ 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, &reg);
+ 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 <argus.lin@mediatek.com>");
+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 000000000000..78ada3a5bfae
--- /dev/null
+++ b/sound/soc/codecs/mt6359-accdet.h
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2021 MediaTek Inc.
+ * Author: Argus Lin <argus.lin@mediatek.com>
+ */
+
+#ifndef _ACCDET_H_
+#define _ACCDET_H_
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#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;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_MT6359_ACCDET)
+int mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack);
+#else
+static inline int
+mt6359_accdet_enable_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+#endif
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
new file mode 100644
index 000000000000..f73120c6a6ce
--- /dev/null
+++ b/sound/soc/codecs/mt6359.c
@@ -0,0 +1,2962 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mt6359.c -- mt6359 ALSA SoC audio codec driver
+//
+// Copyright (c) 2020 MediaTek Inc.
+// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
+
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/mfd/mt6397/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, &reg);
+ 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, "audio-codec");
+ if (!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 <kaichieh.chuang@mediatek.com>");
+MODULE_AUTHOR("Eason Yen <eason.yen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h
new file mode 100644
index 000000000000..296ffa7f50b5
--- /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 <argus.lin@mediatek.com>
+ */
+
+#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 000000000000..d16bccebae52
--- /dev/null
+++ b/sound/soc/codecs/mt6660.c
@@ -0,0 +1,581 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (c) 2019 MediaTek Inc.
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#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 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 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 = {
+ 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" },
+ {},
+};
+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 = pm_ptr(&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 <jeff_chang@richtek.com>");
+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 000000000000..054a3c56ec1f
--- /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 <linux/mutex.h>
+#include <linux/regmap.h>
+
+#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 000000000000..125742601f88
--- /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 <ctlin0@nuvoton.com>
+//
+// Based on MAX98357A.c
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+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 <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c
new file mode 100644
index 000000000000..2266f320a8f2
--- /dev/null
+++ b/sound/soc/codecs/nau8325.c
@@ -0,0 +1,900 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8325.c -- Nuvoton NAU8325 audio codec driver
+//
+// Copyright 2023 Nuvoton Technology Crop.
+// Author: Seven Lee <WTLI@nuvoton.com>
+// David Lin <CTLIN0@nuvoton.com>
+//
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "nau8325.h"
+
+/* Range of Master Clock MCLK (Hz) */
+#define MASTER_CLK_MAX 49152000
+#define MASTER_CLK_MIN 2048000
+
+/* scaling for MCLK source */
+#define CLK_PROC_BYPASS (-1)
+
+/* the maximum CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* from MCLK input */
+#define MCLK_SRC 4
+
+static const struct nau8325_src_attr mclk_n1_div[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 3, 0x2 },
+};
+
+/* over sampling rate */
+static const struct nau8325_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 nau8325_src_attr mclk_n2_div[] = {
+ { 0, 0x0 },
+ { 1, 0x1 },
+ { 2, 0x2 },
+ { 3, 0x3 },
+ { 4, 0x4 },
+};
+
+static const struct nau8325_src_attr mclk_n3_mult[] = {
+ { 0, 0x1 },
+ { 1, 0x2 },
+ { 2, 0x3 },
+ { 3, 0x4 },
+};
+
+/* Sample Rate and MCLK_SRC selections */
+static const struct nau8325_srate_attr target_srate_table[] = {
+ /* { FS, range, max, { MCLK source }} */
+ { 48000, 2, true, { 12288000, 19200000, 24000000 } },
+ { 16000, 1, false, { 4096000, 6400000, 8000000 } },
+ { 8000, 0, false, { 2048000, 3200000, 4000000 }},
+ { 44100, 2, true, { 11289600, 17640000, 22050000 }},
+ { 64000, 3, false, { 16384000, 25600000, 32000000 } },
+ { 96000, 3, true, { 24576000, 38400000, 48000000 } },
+ { 12000, 0, true, { 3072000, 4800000, 6000000 } },
+ { 24000, 1, true, { 6144000, 9600000, 12000000 } },
+ { 32000, 2, false, { 8192000, 12800000, 16000000 } },
+};
+
+static const struct reg_default nau8325_reg_defaults[] = {
+ { NAU8325_R00_HARDWARE_RST, 0x0000 },
+ { NAU8325_R01_SOFTWARE_RST, 0x0000 },
+ { NAU8325_R03_CLK_CTRL, 0x0000 },
+ { NAU8325_R04_ENA_CTRL, 0x0000 },
+ { NAU8325_R05_INTERRUPT_CTRL, 0x007f },
+ { NAU8325_R09_IRQOUT, 0x0000 },
+ { NAU8325_R0A_IO_CTRL, 0x0000 },
+ { NAU8325_R0B_PDM_CTRL, 0x0000 },
+ { NAU8325_R0C_TDM_CTRL, 0x0000 },
+ { NAU8325_R0D_I2S_PCM_CTRL1, 0x000a },
+ { NAU8325_R0E_I2S_PCM_CTRL2, 0x0000 },
+ { NAU8325_R0F_L_TIME_SLOT, 0x0000 },
+ { NAU8325_R10_R_TIME_SLOT, 0x0000 },
+ { NAU8325_R11_HPF_CTRL, 0x0000 },
+ { NAU8325_R12_MUTE_CTRL, 0x0000 },
+ { NAU8325_R13_DAC_VOLUME, 0xf3f3 },
+ { NAU8325_R29_DAC_CTRL1, 0x0081 },
+ { NAU8325_R2A_DAC_CTRL2, 0x0000 },
+ { NAU8325_R2C_ALC_CTRL1, 0x000e },
+ { NAU8325_R2D_ALC_CTRL2, 0x8400 },
+ { NAU8325_R2E_ALC_CTRL3, 0x0000 },
+ { NAU8325_R2F_ALC_CTRL4, 0x003f },
+ { NAU8325_R40_CLK_DET_CTRL, 0xa801 },
+ { NAU8325_R50_MIXER_CTRL, 0x0000 },
+ { NAU8325_R55_MISC_CTRL, 0x0000 },
+ { NAU8325_R60_BIAS_ADJ, 0x0000 },
+ { NAU8325_R61_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8325_R62_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8325_R63_ANALOG_CONTROL_3, 0x0000 },
+ { NAU8325_R64_ANALOG_CONTROL_4, 0x0000 },
+ { NAU8325_R65_ANALOG_CONTROL_5, 0x0000 },
+ { NAU8325_R66_ANALOG_CONTROL_6, 0x0000 },
+ { NAU8325_R69_CLIP_CTRL, 0x0000 },
+ { NAU8325_R73_RDAC, 0x0008 },
+};
+
+static bool nau8325_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R02_DEVICE_ID ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R49_TEST_STATUS ... NAU8325_R4A_ANALOG_READ:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST:
+ case NAU8325_R03_CLK_CTRL ... NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R09_IRQOUT ... NAU8325_R13_DAC_VOLUME:
+ case NAU8325_R29_DAC_CTRL1 ... NAU8325_R2A_DAC_CTRL2:
+ case NAU8325_R2C_ALC_CTRL1 ... NAU8325_R2F_ALC_CTRL4:
+ case NAU8325_R40_CLK_DET_CTRL:
+ case NAU8325_R50_MIXER_CTRL:
+ case NAU8325_R55_MISC_CTRL:
+ case NAU8325_R60_BIAS_ADJ ... NAU8325_R66_ANALOG_CONTROL_6:
+ case NAU8325_R69_CLIP_CTRL:
+ case NAU8325_R73_RDAC:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8325_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8325_R00_HARDWARE_RST ... NAU8325_R02_DEVICE_ID:
+ case NAU8325_R06_INT_CLR_STATUS:
+ case NAU8325_R1D_DEBUG_READ1:
+ case NAU8325_R1F_DEBUG_READ2:
+ case NAU8325_R22_DEBUG_READ3:
+ case NAU8325_R4A_ANALOG_READ:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8325_dac_oversampl_texts[] = {
+ "64", "256", "128", "32",
+};
+
+static const unsigned int nau8325_dac_oversampl_values[] = {
+ 0, 1, 2, 4,
+};
+
+static const struct soc_enum nau8325_dac_oversampl_enum =
+ SOC_VALUE_ENUM_SINGLE(NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_SFT, 0x7,
+ ARRAY_SIZE(nau8325_dac_oversampl_texts),
+ nau8325_dac_oversampl_texts,
+ nau8325_dac_oversampl_values);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(dac_vol_tlv, -8000, 600);
+
+static const struct snd_kcontrol_new nau8325_snd_controls[] = {
+ SOC_ENUM("DAC Oversampling Rate", nau8325_dac_oversampl_enum),
+ SOC_DOUBLE_TLV("Speaker Volume", NAU8325_R13_DAC_VOLUME,
+ NAU8325_DAC_VOLUME_L_SFT, NAU8325_DAC_VOLUME_R_SFT,
+ NAU8325_DAC_VOLUME_R_EN, 0, dac_vol_tlv),
+ SOC_SINGLE("ALC Max Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_SFT, NAU8325_ALC_MAXGAIN_MAX, 0),
+ SOC_SINGLE("ALC Min Gain", NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MINGAIN_SFT, NAU8325_ALC_MINGAIN_MAX, 0),
+ SOC_SINGLE("ALC Decay Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_SFT, NAU8325_ALC_DCY_MAX, 0),
+ SOC_SINGLE("ALC Attack Timer", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_ATK_SFT, NAU8325_ALC_ATK_MAX, 0),
+ SOC_SINGLE("ALC Hold Time", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_HLD_SFT, NAU8325_ALC_HLD_MAX, 0),
+ SOC_SINGLE("ALC Target Level", NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_LVL_SFT, NAU8325_ALC_LVL_MAX, 0),
+ SOC_SINGLE("ALC Enable Switch", NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN_SFT, 1, 0),
+};
+
+static int nau8325_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, 0);
+ msleep(30);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Soft mute the output to prevent the pop noise. */
+ regmap_update_bits(nau8325->regmap, NAU8325_R12_MUTE_CTRL,
+ NAU8325_SOFT_MUTE, NAU8325_SOFT_MUTE);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8325_powerup_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (nau8325->clock_detection)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, NAU8325_PWRUP_DFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_PWRUP_DFT, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget nau8325_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Power Up", SND_SOC_NOPM, 0, 0,
+ nau8325_powerup_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_LEFT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, NAU8325_R04_ENA_CTRL,
+ NAU8325_DAC_RIGHT_CH_EN_SFT, 0, nau8325_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKL"),
+ SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route nau8325_dapm_routes[] = {
+ { "DACL", NULL, "Power Up" },
+ { "DACR", NULL, "Power Up" },
+
+ { "DACL", NULL, "AIFRX" },
+ { "DACR", NULL, "AIFRX" },
+ { "SPKL", NULL, "DACL" },
+ { "SPKR", NULL, "DACR" },
+};
+
+static int nau8325_srate_clk_apply(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int n1_sel, int mclk_mult_sel, int n2_sel)
+{
+ if (!srate_table || n2_sel < 0 || n2_sel >= ARRAY_SIZE(mclk_n2_div) ||
+ n1_sel < 0 || n1_sel >= ARRAY_SIZE(mclk_n1_div)) {
+ dev_dbg(nau8325->dev, "The CLK isn't supported.");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_REG_SRATE_MASK | NAU8325_REG_DIV_MAX,
+ (srate_table->range << NAU8325_REG_SRATE_SFT) |
+ (srate_table->max ? NAU8325_REG_DIV_MAX : 0));
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SRC_MASK, mclk_n2_div[n2_sel].val);
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_MUL_SRC_MASK,
+ mclk_n1_div[n1_sel].val << NAU8325_CLK_MUL_SRC_SFT);
+
+ if (mclk_mult_sel != CLK_PROC_BYPASS) {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK,
+ mclk_n3_mult[mclk_mult_sel].val <<
+ NAU8325_MCLK_SEL_SFT);
+ } else {
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_MCLK_SEL_MASK, 0);
+ }
+
+ switch (mclk_mult_sel) {
+ case 2:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN, NAU8325_MCLK4XEN_EN);
+ break;
+ case 3:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN);
+ break;
+ default:
+ regmap_update_bits(nau8325->regmap, NAU8325_R65_ANALOG_CONTROL_5,
+ NAU8325_MCLK4XEN_EN | NAU8325_MCLK8XEN_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8325_clksrc_n2(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr *srate_table,
+ int mclk, int *n2_sel)
+{
+ int i, mclk_src, ratio;
+
+ ratio = NAU8325_MCLK_FS_RATIO_NUM;
+ for (i = 0; i < ARRAY_SIZE(mclk_n2_div); i++) {
+ mclk_src = mclk >> mclk_n2_div[i].param;
+ if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_256] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_256;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_400] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_400;
+ break;
+ } else if (srate_table->mclk_src[NAU8325_MCLK_FS_RATIO_500] == mclk_src) {
+ ratio = NAU8325_MCLK_FS_RATIO_500;
+ break;
+ }
+ }
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM)
+ *n2_sel = i;
+
+ return ratio;
+}
+
+static const struct nau8325_srate_attr *target_srate_attribute(int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(target_srate_table); i++)
+ if (target_srate_table[i].fs == srate)
+ break;
+
+ if (i == ARRAY_SIZE(target_srate_table))
+ goto proc_err;
+
+ return &target_srate_table[i];
+
+proc_err:
+ return NULL;
+}
+
+static int nau8325_clksrc_choose(struct nau8325 *nau8325,
+ const struct nau8325_srate_attr **srate_table,
+ int *n1_sel, int *mult_sel, int *n2_sel)
+{
+ int i, j, mclk, mclk_max, ratio, ratio_sel, n2_max;
+
+ if (!nau8325->mclk || !nau8325->fs)
+ goto proc_err;
+
+ /* select sampling rate and MCLK_SRC */
+ *srate_table = target_srate_attribute(nau8325->fs);
+ if (!*srate_table)
+ goto proc_err;
+
+ /* First check clock from MCLK directly, decide N2 for MCLK_SRC.
+ * If not good, consider 1/N1 and Multiplier.
+ */
+ ratio = nau8325_clksrc_n2(nau8325, *srate_table, nau8325->mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM) {
+ *n1_sel = 0;
+ *mult_sel = CLK_PROC_BYPASS;
+ *n2_sel = MCLK_SRC;
+ goto proc_done;
+ }
+
+ /* Get MCLK_SRC through 1/N, Multiplier, and then 1/N2. */
+ mclk_max = 0;
+ for (i = 0; i < ARRAY_SIZE(mclk_n1_div); i++) {
+ for (j = 0; j < ARRAY_SIZE(mclk_n3_mult); j++) {
+ mclk = nau8325->mclk << mclk_n3_mult[j].param;
+ mclk = mclk / mclk_n1_div[i].param;
+ ratio = nau8325_clksrc_n2(nau8325,
+ *srate_table, mclk, n2_sel);
+ if (ratio != NAU8325_MCLK_FS_RATIO_NUM &&
+ (mclk_max < mclk || i > *n1_sel)) {
+ mclk_max = mclk;
+ n2_max = *n2_sel;
+ *n1_sel = i;
+ *mult_sel = j;
+ ratio_sel = ratio;
+ goto proc_done;
+ }
+ }
+ }
+ if (mclk_max) {
+ *n2_sel = n2_max;
+ ratio = ratio_sel;
+ goto proc_done;
+ }
+
+proc_err:
+ dev_dbg(nau8325->dev, "The MCLK %d is invalid. It can't get MCLK_SRC of 256/400/500 FS (%d)",
+ nau8325->mclk, nau8325->fs);
+ return -EINVAL;
+proc_done:
+ dev_dbg(nau8325->dev, "nau8325->fs=%d,range=0x%x, %s, (n1,mu,n2,dmu):(%d,%d,%d), MCLK_SRC=%uHz (%d)",
+ nau8325->fs, (*srate_table)->range,
+ (*srate_table)->max ? "MAX" : "MIN",
+ *n1_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : mclk_n1_div[*n1_sel].param,
+ *mult_sel == CLK_PROC_BYPASS ?
+ CLK_PROC_BYPASS : 1 << mclk_n3_mult[*mult_sel].param,
+ 1 << mclk_n2_div[*n2_sel].param,
+ (*srate_table)->mclk_src[ratio],
+ (*srate_table)->mclk_src[ratio] / nau8325->fs);
+
+ return 0;
+}
+
+static int nau8325_clock_config(struct nau8325 *nau8325)
+{
+ const struct nau8325_srate_attr *srate_table;
+ int ret, n1_sel, mult_sel, n2_sel;
+
+ ret = nau8325_clksrc_choose(nau8325, &srate_table,
+ &n1_sel, &mult_sel, &n2_sel);
+ if (ret)
+ goto err;
+
+ ret = nau8325_srate_clk_apply(nau8325, srate_table,
+ n1_sel, mult_sel, n2_sel);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ return ret;
+}
+
+static const struct nau8325_osr_attr *nau8325_get_osr(struct nau8325 *nau8325)
+{
+ unsigned int osr;
+
+ regmap_read(nau8325->regmap, NAU8325_R29_DAC_CTRL1, &osr);
+ osr &= NAU8325_DAC_OVERSAMPLE_MASK;
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return NULL;
+
+ return &osr_dac_sel[osr];
+}
+
+static int nau8325_dai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ const struct nau8325_osr_attr *osr;
+
+ osr = nau8325_get_osr(nau8325);
+ 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 nau8325_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 nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len = 0;
+ const struct nau8325_osr_attr *osr;
+ int ret;
+
+ nau8325->fs = params_rate(params);
+ osr = nau8325_get_osr(nau8325);
+ if (!osr || !osr->osr || nau8325->fs * osr->osr > CLK_DA_AD_MAX) {
+ ret = -EINVAL;
+ goto err;
+ }
+ regmap_update_bits(nau8325->regmap, NAU8325_R03_CLK_CTRL,
+ NAU8325_CLK_DAC_SRC_MASK,
+ osr->clk_src << NAU8325_CLK_DAC_SRC_SFT);
+
+ ret = nau8325_clock_config(nau8325);
+ if (ret)
+ goto err;
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8325_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8325_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8325_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8325_I2S_DL_32;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DL_MASK, val_len);
+
+ return 0;
+
+err:
+ return ret;
+}
+
+static int nau8325_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+ unsigned int ctrl1_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_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;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8325_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8325_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8325_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8325_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8325_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8325_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8325->regmap, NAU8325_R0D_I2S_PCM_CTRL1,
+ NAU8325_I2S_DF_MASK | NAU8325_I2S_BP_MASK |
+ NAU8325_I2S_PCMB_EN, ctrl1_val);
+
+ return 0;
+}
+
+static int nau8325_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
+{
+ struct nau8325 *nau8325 = snd_soc_component_get_drvdata(component);
+
+ if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) {
+ dev_dbg(nau8325->dev, "MCLK exceeds the range, MCLK:%d", freq);
+ return -EINVAL;
+ }
+
+ nau8325->mclk = freq;
+ dev_dbg(nau8325->dev, "MCLK %dHz", nau8325->mclk);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver nau8325_component_driver = {
+ .set_sysclk = nau8325_set_sysclk,
+ .suspend_bias_off = true,
+ .controls = nau8325_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8325_snd_controls),
+ .dapm_widgets = nau8325_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8325_dapm_widgets),
+ .dapm_routes = nau8325_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8325_dapm_routes),
+};
+
+static const struct snd_soc_dai_ops nau8325_dai_ops = {
+ .startup = nau8325_dai_startup,
+ .hw_params = nau8325_hw_params,
+ .set_fmt = nau8325_set_fmt,
+};
+
+#define NAU8325_RATES SNDRV_PCM_RATE_8000_96000
+#define NAU8325_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE)
+
+static struct snd_soc_dai_driver nau8325_dai = {
+ .name = NAU8325_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8325_RATES,
+ .formats = NAU8325_FORMATS,
+ },
+ .ops = &nau8325_dai_ops,
+};
+
+static const struct regmap_config nau8325_regmap_config = {
+ .reg_bits = NAU8325_REG_ADDR_LEN,
+ .val_bits = NAU8325_REG_DATA_LEN,
+
+ .max_register = NAU8325_REG_MAX,
+ .readable_reg = nau8325_readable_reg,
+ .writeable_reg = nau8325_writeable_reg,
+ .volatile_reg = nau8325_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8325_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8325_reg_defaults),
+};
+
+static void nau8325_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0001);
+ regmap_write(regmap, NAU8325_R00_HARDWARE_RST, 0x0000);
+}
+
+static void nau8325_init_regs(struct nau8325 *nau8325)
+{
+ struct regmap *regmap = nau8325->regmap;
+ struct device *dev = nau8325->dev;
+
+ /* set ALC parameters */
+ regmap_update_bits(regmap, NAU8325_R2C_ALC_CTRL1,
+ NAU8325_ALC_MAXGAIN_MASK,
+ 0x7 << NAU8325_ALC_MAXGAIN_SFT);
+ regmap_update_bits(regmap, NAU8325_R2D_ALC_CTRL2,
+ NAU8325_ALC_DCY_MASK | NAU8325_ALC_ATK_MASK |
+ NAU8325_ALC_HLD_MASK, (0x5 << NAU8325_ALC_DCY_SFT) |
+ (0x3 << NAU8325_ALC_ATK_SFT) |
+ (0x5 << NAU8325_ALC_HLD_SFT));
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+
+ /* DAC Reference Voltage Setting */
+ switch (nau8325->dac_vref_microvolt) {
+ case 1800000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 0 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2700000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 1 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 2880000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 2 << NAU8325_DACVREFSEL_SFT);
+ break;
+ case 3060000:
+ regmap_update_bits(regmap, NAU8325_R73_RDAC,
+ NAU8325_DACVREFSEL_MASK, 3 << NAU8325_DACVREFSEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid dac-vref-microvolt %d", nau8325->dac_vref_microvolt);
+
+ }
+
+ /* DAC Reference Voltage Decoupling Capacitors. */
+ regmap_update_bits(regmap, NAU8325_R63_ANALOG_CONTROL_3,
+ NAU8325_CLASSD_COARSE_GAIN_MASK, 0x4);
+ /* Auto-Att Min Gain 0dB, Class-D N Driver Slew Rate -25%. */
+ regmap_update_bits(regmap, NAU8325_R64_ANALOG_CONTROL_4,
+ NAU8325_CLASSD_SLEWN_MASK, 0x7);
+
+ /* VMID Tieoff (VMID Resistor Selection) */
+ switch (nau8325->vref_impedance_ohms) {
+ case 0:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 0 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 25000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 1 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 125000:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 2 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ case 2500:
+ regmap_update_bits(regmap, NAU8325_R60_BIAS_ADJ,
+ NAU8325_BIAS_VMID_SEL_MASK, 3 << NAU8325_BIAS_VMID_SEL_SFT);
+ break;
+ default:
+ dev_dbg(dev, "Invalid vref-impedance-ohms %d", nau8325->vref_impedance_ohms);
+ }
+
+
+ /* enable VMID, BIAS, DAC, DCA CLOCK, Voltage/Current Amps
+ */
+ regmap_update_bits(regmap, NAU8325_R61_ANALOG_CONTROL_1,
+ NAU8325_DACEN_MASK | NAU8325_DACCLKEN_MASK |
+ NAU8325_DACEN_R_MASK | NAU8325_DACCLKEN_R_MASK |
+ NAU8325_CLASSDEN_MASK | NAU8325_VMDFSTENB_MASK |
+ NAU8325_BIASEN_MASK | NAU8325_VMIDEN_MASK,
+ (0x1 << NAU8325_DACEN_SFT) |
+ (0x1 << NAU8325_DACCLKEN_SFT) |
+ (0x1 << NAU8325_DACEN_R_SFT) |
+ (0x1 << NAU8325_DACCLKEN_R_SFT) |
+ (0x1 << NAU8325_CLASSDEN_SFT) |
+ (0x1 << NAU8325_VMDFSTENB_SFT) |
+ (0x1 << NAU8325_BIASEN_SFT) | 0x3);
+
+ /* Enable ALC to avoid signal distortion when battery low. */
+ if (nau8325->alc_enable)
+ regmap_update_bits(regmap, NAU8325_R2E_ALC_CTRL3,
+ NAU8325_ALC_EN, NAU8325_ALC_EN);
+ if (nau8325->clock_det_data)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, NAU8325_APWRUP_EN);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_APWRUP_EN, 0);
+ if (nau8325->clock_detection)
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS |
+ NAU8325_PWRUP_DFT, 0);
+ else
+ regmap_update_bits(regmap, NAU8325_R40_CLK_DET_CTRL,
+ NAU8325_CLKPWRUP_DIS | NAU8325_PWRUP_DFT,
+ NAU8325_CLKPWRUP_DIS);
+ regmap_update_bits(regmap, NAU8325_R29_DAC_CTRL1,
+ NAU8325_DAC_OVERSAMPLE_MASK,
+ NAU8325_DAC_OVERSAMPLE_128);
+}
+
+static void nau8325_print_device_properties(struct nau8325 *nau8325)
+{
+ struct device *dev = nau8325->dev;
+
+ dev_dbg(dev, "vref-impedance-ohms: %d", nau8325->vref_impedance_ohms);
+ dev_dbg(dev, "dac-vref-microvolt: %d", nau8325->dac_vref_microvolt);
+ dev_dbg(dev, "alc-enable: %d", nau8325->alc_enable);
+ dev_dbg(dev, "clock-det-data: %d", nau8325->clock_det_data);
+ dev_dbg(dev, "clock-detection-disable: %d", nau8325->clock_detection);
+}
+
+static int nau8325_read_device_properties(struct device *dev,
+ struct nau8325 *nau8325)
+{
+ int ret;
+
+ nau8325->alc_enable =
+ device_property_read_bool(dev, "nuvoton,alc-enable");
+ nau8325->clock_det_data =
+ device_property_read_bool(dev, "nuvoton,clock-det-data");
+ nau8325->clock_detection =
+ !device_property_read_bool(dev, "nuvoton,clock-detection-disable");
+
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance-ohms",
+ &nau8325->vref_impedance_ohms);
+ if (ret)
+ nau8325->vref_impedance_ohms = 125000;
+ ret = device_property_read_u32(dev, "nuvoton,dac-vref-microvolt",
+ &nau8325->dac_vref_microvolt);
+ if (ret)
+ nau8325->dac_vref_microvolt = 2880000;
+
+ return 0;
+}
+
+static int nau8325_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8325 *nau8325 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8325) {
+ nau8325 = devm_kzalloc(dev, sizeof(*nau8325), GFP_KERNEL);
+ if (!nau8325) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ ret = nau8325_read_device_properties(dev, nau8325);
+ if (ret)
+ goto err;
+ }
+ i2c_set_clientdata(i2c, nau8325);
+
+ nau8325->regmap = devm_regmap_init_i2c(i2c, &nau8325_regmap_config);
+ if (IS_ERR(nau8325->regmap)) {
+ ret = PTR_ERR(nau8325->regmap);
+ goto err;
+ }
+ nau8325->dev = dev;
+ nau8325_print_device_properties(nau8325);
+
+ nau8325_reset_chip(nau8325->regmap);
+ ret = regmap_read(nau8325->regmap, NAU8325_R02_DEVICE_ID, &value);
+ if (ret) {
+ dev_dbg(dev, "Failed to read device id (%d)", ret);
+ goto err;
+ }
+ nau8325_init_regs(nau8325);
+
+ ret = devm_snd_soc_register_component(dev, &nau8325_component_driver,
+ &nau8325_dai, 1);
+err:
+ return ret;
+}
+
+static const struct i2c_device_id nau8325_i2c_ids[] = {
+ { "nau8325" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8325_of_ids[] = {
+ { .compatible = "nuvoton,nau8325", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8325_of_ids);
+#endif
+
+static struct i2c_driver nau8325_i2c_driver = {
+ .driver = {
+ .name = "nau8325",
+ .of_match_table = of_match_ptr(nau8325_of_ids),
+ },
+ .probe = nau8325_i2c_probe,
+ .id_table = nau8325_i2c_ids,
+};
+module_i2c_driver(nau8325_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU8325 driver");
+MODULE_AUTHOR("Seven Lee <WTLI@nuvoton.com>");
+MODULE_AUTHOR("David Lin <CTLIN0@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8325.h b/sound/soc/codecs/nau8325.h
new file mode 100644
index 000000000000..0d173b66a4d4
--- /dev/null
+++ b/sound/soc/codecs/nau8325.h
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * nau8325.h -- Nuvoton NAU8325 audio codec driver
+ *
+ * Copyright 2023 Nuvoton Technology Crop.
+ * Author: Seven Lee <WTLI@nuvoton.com>
+ * David Lin <CTLIN0@nuvoton.com>
+ */
+
+#ifndef __NAU8325_H__
+#define __NAU8325_H__
+
+#define NAU8325_R00_HARDWARE_RST 0x00
+#define NAU8325_R01_SOFTWARE_RST 0x01
+#define NAU8325_R02_DEVICE_ID 0x02
+#define NAU8325_R03_CLK_CTRL 0x03
+#define NAU8325_R04_ENA_CTRL 0x04
+#define NAU8325_R05_INTERRUPT_CTRL 0x05
+#define NAU8325_R06_INT_CLR_STATUS 0x06
+#define NAU8325_R09_IRQOUT 0x09
+#define NAU8325_R0A_IO_CTRL 0x0a
+#define NAU8325_R0B_PDM_CTRL 0x0b
+#define NAU8325_R0C_TDM_CTRL 0x0c
+#define NAU8325_R0D_I2S_PCM_CTRL1 0x0d
+#define NAU8325_R0E_I2S_PCM_CTRL2 0x0e
+#define NAU8325_R0F_L_TIME_SLOT 0x0f
+#define NAU8325_R10_R_TIME_SLOT 0x10
+#define NAU8325_R11_HPF_CTRL 0x11
+#define NAU8325_R12_MUTE_CTRL 0x12
+#define NAU8325_R13_DAC_VOLUME 0x13
+#define NAU8325_R1D_DEBUG_READ1 0x1d
+#define NAU8325_R1F_DEBUG_READ2 0x1f
+#define NAU8325_R22_DEBUG_READ3 0x22
+#define NAU8325_R29_DAC_CTRL1 0x29
+#define NAU8325_R2A_DAC_CTRL2 0x2a
+#define NAU8325_R2C_ALC_CTRL1 0x2c
+#define NAU8325_R2D_ALC_CTRL2 0x2d
+#define NAU8325_R2E_ALC_CTRL3 0x2e
+#define NAU8325_R2F_ALC_CTRL4 0x2f
+#define NAU8325_R40_CLK_DET_CTRL 0x40
+#define NAU8325_R49_TEST_STATUS 0x49
+#define NAU8325_R4A_ANALOG_READ 0x4a
+#define NAU8325_R50_MIXER_CTRL 0x50
+#define NAU8325_R55_MISC_CTRL 0x55
+#define NAU8325_R60_BIAS_ADJ 0x60
+#define NAU8325_R61_ANALOG_CONTROL_1 0x61
+#define NAU8325_R62_ANALOG_CONTROL_2 0x62
+#define NAU8325_R63_ANALOG_CONTROL_3 0x63
+#define NAU8325_R64_ANALOG_CONTROL_4 0x64
+#define NAU8325_R65_ANALOG_CONTROL_5 0x65
+#define NAU8325_R66_ANALOG_CONTROL_6 0x66
+#define NAU8325_R69_CLIP_CTRL 0x69
+#define NAU8325_R73_RDAC 0x73
+#define NAU8325_REG_MAX NAU8325_R73_RDAC
+
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8325_REG_ADDR_LEN 16
+#define NAU8325_REG_DATA_LEN 16
+
+/* CLK_CTRL (0x03) */
+#define NAU8325_CLK_DAC_SRC_SFT 12
+#define NAU8325_CLK_DAC_SRC_MASK (0x3 << NAU8325_CLK_DAC_SRC_SFT)
+#define NAU8325_CLK_MUL_SRC_SFT 6
+#define NAU8325_CLK_MUL_SRC_MASK (0x3 << NAU8325_CLK_MUL_SRC_SFT)
+#define NAU8325_MCLK_SEL_SFT 3
+#define NAU8325_MCLK_SEL_MASK (0x7 << NAU8325_MCLK_SEL_SFT)
+#define NAU8325_MCLK_SRC_MASK 0x7
+
+/* ENA_CTRL (0x04) */
+#define NAU8325_DAC_LEFT_CH_EN_SFT 3
+#define NAU8325_DAC_LEFT_CH_EN (0x1 << NAU8325_DAC_LEFT_CH_EN_SFT)
+#define NAU8325_DAC_RIGHT_CH_EN_SFT 2
+#define NAU8325_DAC_RIGHT_CH_EN (0x1 << NAU8325_DAC_RIGHT_CH_EN_SFT)
+
+/* INTERRUPT_CTRL (0x05) */
+#define NAU8325_ARP_DWN_INT_SFT 12
+#define NAU8325_ARP_DWN_INT_MASK (0x1 << NAU8325_ARP_DWN_INT_SFT)
+#define NAU8325_CLIP_INT_SFT 11
+#define NAU8325_CLIP_INT_MASK (0x1 << NAU8325_CLIP_INT_SFT)
+#define NAU8325_LVD_INT_SFT 10
+#define NAU8325_LVD_INT_MASK (0x1 << NAU8325_LVD_INT_SFT)
+#define NAU8325_PWR_INT_DIS_SFT 8
+#define NAU8325_PWR_INT_DIS (0x1 << NAU8325_PWR_INT_DIS_SFT)
+#define NAU8325_OCP_OTP_SHTDWN_INT_SFT 4
+#define NAU8325_OCP_OTP_SHTDWN_INT_MASK (0x1 << NAU8325_OCP_OTP_SHTDWN_INT_SFT)
+#define NAU8325_CLIP_INT_DIS_SFT 3
+#define NAU8325_CLIP_INT_DIS (0x1 << NAU8325_CLIP_INT_DIS_SFT)
+#define NAU8325_LVD_INT_DIS_SFT 2
+#define NAU8325_LVD_INT_DIS (0x1 << NAU8325_LVD_INT_DIS_SFT)
+#define NAU8325_PWR_INT_MASK 0x1
+
+/* INT_CLR_STATUS (0x06) */
+#define NAU8325_INT_STATUS_MASK 0x7f
+
+/* IRQOUT (0x9) */
+#define NAU8325_IRQOUT_SEL_SEF 12
+#define NAU8325_IRQOUT_SEL_MASK (0xf << NAU8325_IRQOUT_SEL_SEF)
+#define NAU8325_DEM_DITH_SFT 7
+#define NAU8325_DEM_DITH_EN (0x1 << NAU8325_DEM_DITH_SFT)
+#define NAU8325_GAINZI3_SFT 5
+#define NAU8325_GAINZI3_MASK (0x1 << NAU8325_GAINZI3_SFT)
+#define NAU8325_GAINZI2_MASK 0x1f
+
+/* IO_CTRL (0x0a) */
+#define NAU8325_IRQ_PL_SFT 15
+#define NAU8325_IRQ_PL_ACT_HIGH (0x1 << NAU8325_IRQ_PL_SFT)
+#define NAU8325_IRQ_PS_SFT 14
+#define NAU8325_IRQ_PS_UP (0x1 << NAU8325_IRQ_PS_SFT)
+#define NAU8325_IRQ_PE_SFT 13
+#define NAU8325_IRQ_PE_EN (0x1 << NAU8325_IRQ_PE_SFT)
+#define NAU8325_IRQ_DS_SFT 12
+#define NAU8325_IRQ_DS_HIGH (0x1 << NAU8325_IRQ_DS_SFT)
+#define NAU8325_IRQ_OUTPUT_SFT 11
+#define NAU8325_IRQ_OUTPUT_EN (0x1 << NAU8325_IRQ_OUTPUT_SFT)
+#define NAU8325_IRQ_PIN_DEBUG_SFT 10
+#define NAU8325_IRQ_PIN_DEBUG_EN (0x1 << NAU8325_IRQ_PIN_DEBUG_SFT)
+
+/* PDM_CTRL (0x0b) */
+#define NAU8325_PDM_LCH_EDGE_SFT 1
+#define NAU8325_PDM_LCH_EDGE__MASK (0x1 << NAU8325_PDM_LCH_EDGE_SFT)
+#define NAU8325_PDM_MODE_EN 0x1
+
+/* TDM_CTRL (0x0c) */
+#define NAU8325_TDM_SFT 15
+#define NAU8325_TDM_EN (0x1 << NAU8325_TDM_SFT)
+#define NAU8325_PCM_OFFSET_CTRL_SFT 14
+#define NAU8325_PCM_OFFSET_CTRL_EN (0x1 << NAU8325_PCM_OFFSET_CTRL_SFT)
+#define NAU8325_DAC_LEFT_SFT 6
+#define NAU8325_NAU8325_DAC_LEFT_MASK (0x7 << NAU8325_DAC_LEFT_SFT)
+#define NAU8325_DAC_RIGHT_SFT 3
+#define NAU8325_DAC_RIGHT_MASK (0x7 << NAU8325_DAC_RIGHT_SFT)
+
+/* I2S_PCM_CTRL1 (0x0d) */
+#define NAU8325_DACCM_CTL_SFT 14
+#define NAU8325_DACCM_CTL_MASK (0x3 << NAU8325_DACCM_CTL_SFT)
+#define NAU8325_CMB8_0_SFT 10
+#define NAU8325_CMB8_0_MASK (0x1 << NAU8325_CMB8_0_SFT)
+#define NAU8325_UA_OFFSET_SFT 9
+#define NAU8325_UA_OFFSET_MASK (0x1 << NAU8325_UA_OFFSET_SFT)
+#define NAU8325_I2S_BP_SFT 7
+#define NAU8325_I2S_BP_MASK (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_BP_INV (0x1 << NAU8325_I2S_BP_SFT)
+#define NAU8325_I2S_PCMB_SFT 6
+#define NAU8325_I2S_PCMB_EN (0x1 << NAU8325_I2S_PCMB_SFT)
+#define NAU8325_I2S_DACPSHS0_SFT 5
+#define NAU8325_I2S_DACPSHS0_MASK (0x1 << NAU8325_I2S_DACPSHS0_SFT)
+#define NAU8325_I2S_DL_SFT 2
+#define NAU8325_I2S_DL_MASK (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_32 (0x3 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_24 (0x2 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_20 (0x1 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DL_16 (0x0 << NAU8325_I2S_DL_SFT)
+#define NAU8325_I2S_DF_MASK 0x3
+#define NAU8325_I2S_DF_RIGTH 0x0
+#define NAU8325_I2S_DF_LEFT 0x1
+#define NAU8325_I2S_DF_I2S 0x2
+#define NAU8325_I2S_DF_PCM_AB 0x3
+
+/* I2S_PCM_CTRL2 (0x0e) */
+#define NAU8325_PCM_TS_SFT 10
+#define NAU8325_PCM_TS_EN (0x1 << NAU8325_PCM_TS_SFT)
+#define NAU8325_PCM8BIT0_SFT 8
+#define NAU8325_PCM8BIT0_MASK (0x1 << NAU8325_PCM8BIT0_SFT)
+
+/* L_TIME_SLOT (0x0f)*/
+#define NAU8325_SHORT_FS_DET_SFT 13
+#define NAU8325_SHORT_FS_DET_DIS (0x1 << NAU8325_SHORT_FS_DET_SFT)
+#define NAU8325_TSLOT_L0_MASK 0x3ff
+
+/* R_TIME_SLOT (0x10)*/
+#define NAU8325_TSLOT_R0_MASK 0x3ff
+
+/* HPF_CTRL (0x11)*/
+#define NAU8325_DAC_HPF_SFT 15
+#define NAU8325_DAC_HPF_EN (0x1 << NAU8325_DAC_HPF_SFT)
+#define NAU8325_DAC_HPF_APP_SFT 14
+#define NAU8325_DAC_HPF_APP_MASK (0x1 << NAU8325_DAC_HPF_APP_SFT)
+#define NAU8325_DAC_HPF_FCUT_SFT 11
+#define NAU8325_DAC_HPF_FCUT_MASK (0x7 << NAU8325_DAC_HPF_FCUT_SFT)
+
+/* MUTE_CTRL (0x12)*/
+#define NAU8325_SOFT_MUTE_SFT 15
+#define NAU8325_SOFT_MUTE (0x1 << NAU8325_SOFT_MUTE_SFT)
+#define NAU8325_DAC_ZC_SFT 8
+#define NAU8325_DAC_ZC_EN (0x1 << NAU8325_DAC_ZC_SFT)
+#define NAU8325_UNMUTE_CTL_SFT 6
+#define NAU8325_UNMUTE_CTL_MASK (0x3 << NAU8325_UNMUTE_CTL_SFT)
+#define NAU8325_ANA_MUTE_SFT 4
+#define NAU8325_ANA_MUTE_MASK (0x3 << NAU8325_ANA_MUTE_SFT)
+#define NAU8325_AUTO_MUTE_SFT 3
+#define NAU8325_AUTO_MUTE_DIS (0x1 << NAU8325_AUTO_MUTE_SFT)
+
+/* DAC_VOLUME (0x13) */
+#define NAU8325_DAC_VOLUME_L_SFT 8
+#define NAU8325_DAC_VOLUME_L_EN (0xff << NAU8325_DAC_VOLUME_L_SFT)
+#define NAU8325_DAC_VOLUME_R_SFT 0
+#define NAU8325_DAC_VOLUME_R_EN (0xff << NAU8325_DAC_VOLUME_R_SFT)
+#define NAU8325_DAC_VOL_MAX 0xff
+
+/* DEBUG_READ1 (0x1d)*/
+#define NAU8325_OSR100_MASK (0x1 << 6)
+#define NAU8325_MIPS500_MASK (0x1 << 5)
+#define NAU8325_SHUTDWNDRVR_R_MASK (0x1 << 4)
+#define NAU8325_SHUTDWNDRVR_L_MASK (0x1 << 3)
+#define NAU8325_MUTEB_MASK (0x1 << 2)
+#define NAU8325_PDOSCB_MASK (0x1 << 1)
+#define NAU8325_POWERDOWN1B_D_MASK 0x1
+
+/* DEBUG_READ2 (0x1f)*/
+#define NAU8325_R_CHANNEL_Vol_SFT 8
+#define NAU8325_R_CHANNEL_Vol_MASK (0xff << NAU8325_R_CHANNEL_Vol_SFT)
+#define NAU8325_L_CHANNEL_Vol_MASK 0xff
+
+/* DEBUG_READ3(0x22)*/
+#define NAU8325_PGAL_GAIN_MASK (0x3f << 7)
+#define NAU8325_CLIP_MASK (0x1 << 6)
+#define NAU8325_SCAN_MODE_MASK (0x1 << 5)
+#define NAU8325_SDB_MASK (0x1 << 4)
+#define NAU8325_TALARM_MASK (0x1 << 3)
+#define NAU8325_SHORTR_MASK (0x1 << 2)
+#define NAU8325_SHORTL_MASK (0x1 << 1)
+#define NAU8325_TMDET_MASK 0x1
+
+/* DAC_CTRL1 (0x29) */
+#define NAU8325_DAC_OVERSAMPLE_SFT 0
+#define NAU8325_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8325_DAC_OVERSAMPLE_256 1
+#define NAU8325_DAC_OVERSAMPLE_128 2
+#define NAU8325_DAC_OVERSAMPLE_64 0
+#define NAU8325_DAC_OVERSAMPLE_32 4
+
+/* ALC_CTRL1 (0x2c) */
+#define NAU8325_ALC_MAXGAIN_SFT 5
+#define NAU8325_ALC_MAXGAIN_MAX 0x7
+#define NAU8325_ALC_MAXGAIN_MASK (0x7 << NAU8325_ALC_MAXGAIN_SFT)
+#define NAU8325_ALC_MINGAIN_MAX 4
+#define NAU8325_ALC_MINGAIN_SFT 1
+#define NAU8325_ALC_MINGAIN_MASK (0x7 << NAU8325_ALC_MINGAIN_SFT)
+
+/* ALC_CTRL2 (0x2d) */
+#define NAU8325_ALC_DCY_SFT 12
+#define NAU8325_ALC_DCY_MAX 0xb
+#define NAU8325_ALC_DCY_MASK (0xf << NAU8325_ALC_DCY_SFT)
+#define NAU8325_ALC_ATK_SFT 8
+#define NAU8325_ALC_ATK_MAX 0xb
+#define NAU8325_ALC_ATK_MASK (0xf << NAU8325_ALC_ATK_SFT)
+#define NAU8325_ALC_HLD_SFT 4
+#define NAU8325_ALC_HLD_MAX 0xa
+#define NAU8325_ALC_HLD_MASK (0xf << NAU8325_ALC_HLD_SFT)
+#define NAU8325_ALC_LVL_SFT 0
+#define NAU8325_ALC_LVL_MAX 0xf
+#define NAU8325_ALC_LVL_MASK 0xf
+
+/* ALC_CTRL3 (0x2e) */
+#define NAU8325_ALC_EN_SFT 15
+#define NAU8325_ALC_EN (0x1 << NAU8325_ALC_EN_SFT)
+
+/* TEMP_COMP_CTRL (0x30) */
+#define NAU8325_TEMP_COMP_ACT2_MASK 0xff
+
+/* LPF_CTRL (0x33) */
+#define NAU8325_LPF_IN1_EN_SFT 15
+#define NAU8325_LPF_IN1_EN (0x1 << NAU8325_LPF_IN1_EN_SFT)
+#define NAU8325_LPF_IN1_TC_SFT 11
+#define NAU8325_LPF_IN1_TC_MASK (0xf << NAU8325_LPF_IN1_TC_SFT)
+#define NAU8325_LPF_IN2_EN_SFT 10
+#define NAU8325_LPF_IN2_EN (0x1 << NAU8325_LPF_IN2_EN_SFT)
+#define NAU8325_LPF_IN2_TC_SFT 6
+#define NAU8325_LPF_IN2_TC_MASK (0xf << NAU8325_LPF_IN2_TC_SFT)
+
+/* CLK_DET_CTRL (0x40) */
+#define NAU8325_APWRUP_SFT 15
+#define NAU8325_APWRUP_EN (0x1 << NAU8325_APWRUP_SFT)
+#define NAU8325_CLKPWRUP_SFT 14
+#define NAU8325_CLKPWRUP_DIS (0x1 << NAU8325_CLKPWRUP_SFT)
+#define NAU8325_PWRUP_DFT_SFT 13
+#define NAU8325_PWRUP_DFT (0x1 << NAU8325_PWRUP_DFT_SFT)
+#define NAU8325_REG_SRATE_SFT 10
+#define NAU8325_REG_SRATE_MASK (0x7 << NAU8325_REG_SRATE_SFT)
+#define NAU8325_REG_ALT_SRATE_SFT 9
+#define NAU8325_REG_ALT_SRATE_EN (0x1 << NAU8325_REG_ALT_SRATE_SFT)
+#define NAU8325_REG_DIV_MAX 0x1
+
+/* BIAS_ADJ (0x60) */
+#define NAU8325_BIAS_VMID_SEL_SFT 4
+#define NAU8325_BIAS_VMID_SEL_MASK (0x3 << NAU8325_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_1 (0x61) */
+#define NAU8325_VMDFSTENB_SFT 14
+#define NAU8325_VMDFSTENB_MASK (0x3 << NAU8325_VMDFSTENB_SFT)
+#define NAU8325_CLASSDEN_SFT 12
+#define NAU8325_CLASSDEN_MASK (0x3 << NAU8325_CLASSDEN_SFT)
+#define NAU8325_DACCLKEN_R_SFT 10
+#define NAU8325_DACCLKEN_R_MASK (0x3 << NAU8325_DACCLKEN_R_SFT)
+#define NAU8325_DACEN_R_SFT 8
+#define NAU8325_DACEN_R_MASK (0x3 << NAU8325_DACEN_R_SFT)
+#define NAU8325_DACCLKEN_SFT 6
+#define NAU8325_DACCLKEN_MASK (0x3 << NAU8325_DACCLKEN_SFT)
+#define NAU8325_DACEN_SFT 4
+#define NAU8325_DACEN_MASK (0x3 << NAU8325_DACEN_SFT)
+#define NAU8325_BIASEN_SFT 2
+#define NAU8325_BIASEN_MASK (0x3 << NAU8325_BIASEN_SFT)
+#define NAU8325_VMIDEN_MASK 0x3
+
+/* ANALOG_CONTROL_2 (0x62) */
+#define NAU8325_PWMMOD_SFT 14
+#define NAU8325_PWMMOD_MASK (0x1 << NAU8325_PWMMOD_SFT)
+#define NAU8325_DACTEST_SFT 6
+#define NAU8325_DACTEST_MASK (0x3 << NAU8325_DACTEST_SFT)
+#define NAU8325_DACREFCAP_SFT 4
+#define NAU8325_DACREFCAP_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+
+/* ANALOG_CONTROL_3 (0x63) */
+#define NAU8325_POWER_DOWN_L_SFT 12
+#define NAU8325_POWER_DOWN_L_MASK (0x3 << NAU8325_POWER_DOWN_L_SFT)
+#define NAU8325_POWER_DOWN_R_SFT 11
+#define NAU8325_POWER_DOWN_R_MASK (0x3 << NAU8325_DACREFCAP_SFT)
+#define NAU8325_CLASSD_FINE_SFT 5
+#define NAU8325_CLASSD_FINE_MASK (0x3 << NAU8325_CLASSD_FINE_SFT)
+#define NAU8325_CLASSD_COARSE_GAIN_MASK 0xf
+
+/* ANALOG_CONTROL_4 (0x64) */
+#define NAU8325_CLASSD_OCPN_SFT 12
+#define NAU8325_CLASSD_OCPN_MASK (0xf << NAU8325_CLASSD_OCPN_SFT)
+#define NAU8325_CLASSD_OCPP_SFT 8
+#define NAU8325_CLASSD_OCPP_MASK (0xf << NAU8325_CLASSD_OCPP_SFT)
+#define NAU8325_CLASSD_SLEWN_MASK 0xff
+
+/* ANALOG_CONTROL_5 (0x65) */
+#define NAU8325_MCLK_RANGE_SFT 2
+#define NAU8325_MCLK_RANGE_EN (0x1 << NAU8325_MCLK_RANGE_SFT)
+#define NAU8325_MCLK8XEN_SFT 1
+#define NAU8325_MCLK8XEN_EN (0x1 << NAU8325_MCLK8XEN_SFT)
+#define NAU8325_MCLK4XEN_EN 0x1
+
+/* ANALOG_CONTROL_6 (0x66) */
+#define NAU8325_VBATLOW_SFT 4
+#define NAU8325_VBATLOW_MASK (0x1 << NAU8325_VBATLOW_SFT)
+#define NAU8325_VDDSPK_LIM_SFT 3
+#define NAU8325_VDDSPK_LIM_EN (0x1 << NAU8325_VDDSPK_LIM_SFT)
+#define NAU8325_VDDSPK_LIM_MASK 0x7
+
+/* CLIP_CTRL (0x69)*/
+#define NAU8325_ANTI_CLIP_SFT 4
+#define NAU8325_ANTI_CLIP_EN (0x1 << NAU8325_ANTI_CLIP_SFT)
+
+/* RDAC (0x73) */
+#define NAU8325_CLK_DAC_DELAY_SFT 4
+#define NAU8325_CLK_DAC_DELAY_EN (0x7 << NAU8325_CLK_DAC_DELAY_SFT)
+#define NAU8325_DACVREFSEL_SFT 2
+#define NAU8325_DACVREFSEL_MASK (0x3 << NAU8325_DACVREFSEL_SFT)
+
+#define NAU8325_CODEC_DAI "nau8325-hifi"
+
+struct nau8325 {
+ struct device *dev;
+ struct regmap *regmap;
+ int mclk;
+ int fs;
+ int vref_impedance_ohms;
+ int dac_vref_microvolt;
+ int clock_detection;
+ int clock_det_data;
+ int alc_enable;
+};
+
+struct nau8325_src_attr {
+ int param;
+ unsigned int val;
+};
+
+enum {
+ NAU8325_MCLK_FS_RATIO_256,
+ NAU8325_MCLK_FS_RATIO_400,
+ NAU8325_MCLK_FS_RATIO_500,
+ NAU8325_MCLK_FS_RATIO_NUM,
+};
+
+struct nau8325_srate_attr {
+ int fs;
+ int range;
+ bool max;
+ unsigned int mclk_src[NAU8325_MCLK_FS_RATIO_NUM];
+};
+
+struct nau8325_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+#endif /* __NAU8325_H__ */
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
new file mode 100644
index 000000000000..caf2edb23088
--- /dev/null
+++ b/sound/soc/codecs/nau8540.c
@@ -0,0 +1,993 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#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 nau8540_fepga_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 nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, NAU8540_ACDC_CTL_MIC1P_VREF |
+ NAU8540_ACDC_CTL_MIC1N_VREF | NAU8540_ACDC_CTL_MIC2P_VREF |
+ NAU8540_ACDC_CTL_MIC2N_VREF | NAU8540_ACDC_CTL_MIC3P_VREF |
+ NAU8540_ACDC_CTL_MIC3N_VREF | NAU8540_ACDC_CTL_MIC4P_VREF |
+ NAU8540_ACDC_CTL_MIC4N_VREF);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int nau8540_precharge_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 nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, NAU8540_DISCHRG_EN);
+ msleep(40);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_REFERENCE,
+ NAU8540_DISCHRG_EN, 0);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FEPGA2,
+ NAU8540_ACDC_CTL_MASK, 0);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+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(160);
+ /* DO12 and DO34 pad output enable */
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, NAU8540_ADC_ALL_EN);
+ 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);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_POWER_MANAGEMENT,
+ NAU8540_ADC_ALL_EN, 0);
+ }
+ 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_S("Frontend PGA1", 0, NAU8540_REG_PWR, 12, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA2", 0, NAU8540_REG_PWR, 13, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA3", 0, NAU8540_REG_PWR, 14, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Frontend PGA4", 0, NAU8540_REG_PWR, 15, 0,
+ nau8540_fepga_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("Precharge", 1, SND_SOC_NOPM, 0, 0,
+ nau8540_precharge_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_PGA_S("ADC CH1", 2, NAU8540_REG_ANALOG_PWR, 0, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH2", 2, NAU8540_REG_ANALOG_PWR, 1, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH3", 2, NAU8540_REG_ANALOG_PWR, 2, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_S("ADC CH4", 2, NAU8540_REG_ANALOG_PWR, 3, 0,
+ adc_power_control, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ 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"},
+
+ {"Precharge", NULL, "Frontend PGA1"},
+ {"Precharge", NULL, "Frontend PGA2"},
+ {"Precharge", NULL, "Frontend PGA3"},
+ {"Precharge", NULL, "Frontend PGA4"},
+
+ {"ADC CH1", NULL, "Precharge"},
+ {"ADC CH2", NULL, "Precharge"},
+ {"ADC CH3", NULL, "Precharge"},
+ {"ADC CH4", NULL, "Precharge"},
+
+ {"ADC CH1", NULL, "MICBIAS1"},
+ {"ADC CH2", NULL, "MICBIAS1"},
+ {"ADC CH3", NULL, "MICBIAS2"},
+ {"ADC CH4", 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_CBP_CFP:
+ ctrl2_val |= NAU8540_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 |= 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 int nau8540_dai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8540->regmap;
+ unsigned int val;
+ int ret = 0;
+
+ /* Reading the peak data to detect abnormal data in the ADC channel.
+ * If abnormal data happens, the driver takes recovery actions to
+ * refresh the ADC channel.
+ */
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, NAU8540_CLK_AGC_EN);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, NAU8540_ALC_CH_ALL_EN);
+
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "1.ADC CH1 peak data %x", val);
+ if (!val) {
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, NAU8540_PGA_CH_ALL_MUTE);
+ regmap_update_bits(regmap, NAU8540_REG_MUTE,
+ NAU8540_PGA_CH_ALL_MUTE, 0);
+ regmap_write(regmap, NAU8540_REG_RST, 0x1);
+ regmap_write(regmap, NAU8540_REG_RST, 0);
+ regmap_read(regmap, NAU8540_REG_PEAK_CH1, &val);
+ dev_dbg(nau8540->dev, "2.ADC CH1 peak data %x", val);
+ if (!val) {
+ dev_err(nau8540->dev, "Channel recovery failed!!");
+ ret = -EIO;
+ }
+ }
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_AGC_EN, 0);
+ regmap_update_bits(regmap, NAU8540_REG_ALC_CONTROL_3,
+ NAU8540_ALC_CH_ALL_EN, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+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,
+ .trigger = nau8540_dai_trigger,
+};
+
+#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" },
+ { }
+};
+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 <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
new file mode 100644
index 000000000000..762bb93b06fd
--- /dev/null
+++ b/sound/soc/codecs/nau8540.h
@@ -0,0 +1,265 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU85L40 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#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_ADC_ALL_EN 0xf
+#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_AGC_EN (0x1 << 3)
+#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
+
+/* ALC_CONTROL_3 (0x22) */
+#define NAU8540_ALC_CH1_EN (0x1 << 12)
+#define NAU8540_ALC_CH2_EN (0x1 << 13)
+#define NAU8540_ALC_CH3_EN (0x1 << 14)
+#define NAU8540_ALC_CH4_EN (0x1 << 15)
+#define NAU8540_ALC_CH_ALL_EN (0xf << 12)
+
+/* 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)
+
+/* MUTE (0x61) */
+#define NAU8540_PGA_CH1_MUTE 0x1
+#define NAU8540_PGA_CH2_MUTE 0x2
+#define NAU8540_PGA_CH3_MUTE 0x4
+#define NAU8540_PGA_CH4_MUTE 0x8
+#define NAU8540_PGA_CH_ALL_MUTE 0xf
+
+/* MIC_BIAS (0x67) */
+#define NAU8540_PU_PRE (0x1 << 8)
+
+/* REFERENCE (0x68) */
+#define NAU8540_PRECHARGE_DIS (0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_DISCHRG_EN (0x1 << 11)
+
+/* 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)
+#define NAU8540_ACDC_CTL_SFT 8
+#define NAU8540_ACDC_CTL_MASK (0xff << NAU8540_ACDC_CTL_SFT)
+#define NAU8540_ACDC_CTL_MIC4N_VREF (0x1 << 15)
+#define NAU8540_ACDC_CTL_MIC4P_VREF (0x1 << 14)
+#define NAU8540_ACDC_CTL_MIC3N_VREF (0x1 << 13)
+#define NAU8540_ACDC_CTL_MIC3P_VREF (0x1 << 12)
+#define NAU8540_ACDC_CTL_MIC2N_VREF (0x1 << 11)
+#define NAU8540_ACDC_CTL_MIC2P_VREF (0x1 << 10)
+#define NAU8540_ACDC_CTL_MIC1N_VREF (0x1 << 9)
+#define NAU8540_ACDC_CTL_MIC1P_VREF (0x1 << 8)
+
+/* 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 000000000000..6f432b992941
--- /dev/null
+++ b/sound/soc/codecs/nau8810.c
@@ -0,0 +1,928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * nau8810.c -- NAU8810 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *
+ * Based on WM8974.c
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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;
+ __be16 tmp;
+
+ 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, &reg_val);
+ /* 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 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;
+ __be16 *tmp;
+
+ 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
+ */
+ tmp = (__be16 *)(val + i);
+ value = be16_to_cpup(tmp);
+ 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_CBP_CFP:
+ ctrl2_val |= NAU8810_CLKIO_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { "nau8812" },
+ { "nau8814" },
+ { }
+};
+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 <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8810.h b/sound/soc/codecs/nau8810.h
new file mode 100644
index 000000000000..6a7cacbe044a
--- /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 <ctlin0@nuvoton.com>
+ */
+
+#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 000000000000..edb95f869a4a
--- /dev/null
+++ b/sound/soc/codecs/nau8821.c
@@ -0,0 +1,1959 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// nau8821.c -- Nuvoton NAU88L21 audio codec driver
+//
+// Copyright 2021 Nuvoton Technology Corp.
+// Author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Lee <wtli@nuvoton.com>
+//
+
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/math64.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#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;
+
+ return regmap_raw_read(component->regmap, NAU8821_R21_BIQ0_COF1,
+ ucontrol->value.bytes.data, params->max);
+}
+
+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;
+ int ret;
+
+ if (!component->regmap)
+ return -EINVAL;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(component->regmap, NAU8821_R21_BIQ0_COF1,
+ data, params->max);
+
+ kfree(data);
+
+ return ret;
+}
+
+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(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ 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(nau8821->adc_delay);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ 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 {
+ snd_soc_component_disable_pin(component, "MICBIAS");
+ snd_soc_dapm_sync(nau8821->dapm);
+ }
+ } 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);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8821->adc_delay);
+}
+
+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;
+ ret = device_property_read_u32(dev, "nuvoton,dmic-slew-rate",
+ &nau8821->dmic_slew_rate);
+ if (ret)
+ nau8821->dmic_slew_rate = 0;
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms",
+ &nau8821->adc_delay);
+ if (ret)
+ nau8821->adc_delay = 125;
+ if (nau8821->adc_delay < 125 || nau8821->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
+
+ 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);
+ regmap_update_bits(regmap, NAU8821_R13_DMIC_CTRL,
+ NAU8821_DMIC_SLEW_MASK, nau8821->dmic_slew_rate <<
+ NAU8821_DMIC_SLEW_SFT);
+ 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" },
+ { }
+};
+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 <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
new file mode 100644
index 000000000000..f0935ffafcbe
--- /dev/null
+++ b/sound/soc/codecs/nau8821.h
@@ -0,0 +1,586 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L21 ALSA SoC audio driver
+ *
+ * Copyright 2021 Nuvoton Technology Corp.
+ * Author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Lee <wtli@nuvoton.com>
+ */
+
+#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
+#define NAU8821_DMIC_SLEW_SFT 8
+#define NAU8821_DMIC_SLEW_MASK (0x7 << NAU8821_DMIC_SLEW_SFT)
+
+/* 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 dmic_slew_rate;
+ int key_enable;
+ int adc_delay;
+};
+
+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 000000000000..15d6f8d01f78
--- /dev/null
+++ b/sound/soc/codecs/nau8822.c
@@ -0,0 +1,1221 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// nau8822.c -- NAU8822 ALSA Soc Audio driver
+//
+// Copyright 2017 Nuvoton Technology Crop.
+//
+// Author: David Lin <ctlin0@nuvoton.com>
+// Co-author: John Hsu <kchsu0@nuvoton.com>
+// Co-author: Seven Li <wtli@nuvoton.com>
+//
+// Based on WM8974.c
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+#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_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_dbg(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_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);
+ unsigned long mclk_freq;
+
+ nau8822->div_id = clk_id;
+ nau8822->sysclk = freq;
+
+ if (nau8822->mclk) {
+ mclk_freq = clk_get_rate(nau8822->mclk);
+ if (mclk_freq != freq) {
+ int ret = nau8822_set_pll(dai, NAU8822_CLK_MCLK,
+ NAU8822_CLK_MCLK, mclk_freq, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set PLL\n");
+ return ret;
+ }
+ nau8822->div_id = NAU8822_CLK_PLL;
+ }
+ }
+
+ dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq,
+ nau8822->div_id == NAU8822_CLK_PLL ? "PLL" : "MCLK");
+
+ 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_CBP_CFP:
+ ctrl2_val |= 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 div = 0, 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)
+ div = nau8822->pll.mclk_scaler;
+
+ nau8822_config_clkdiv(dai, div, 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)
+{
+ struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (nau8822->mclk &&
+ snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_ON) {
+ int ret = clk_prepare_enable(nau8822->mclk);
+
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable MCLK: %d\n", ret);
+ return ret;
+ }
+ }
+
+ snd_soc_component_update_bits(component,
+ NAU8822_REG_POWER_MANAGEMENT_1,
+ NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (nau8822->mclk &&
+ snd_soc_component_get_bias_level(component) != SND_SOC_BIAS_OFF)
+ clk_disable_unprepare(nau8822->mclk);
+
+ 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->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(nau8822->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk),
+ "Error getting mclk\n");
+
+ 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" },
+ { }
+};
+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 <ctlin0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
new file mode 100644
index 000000000000..13fe0a091e9e
--- /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 <ctlin0@nuvoton.com>
+ * Co-author: John Hsu <kchsu0@nuvoton.com>
+ * Co-author: Seven Li <wtli@nuvoton.com>
+ */
+
+#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;
+ struct clk *mclk;
+ 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 000000000000..542bd22e6180
--- /dev/null
+++ b/sound/soc/codecs/nau8824.c
@@ -0,0 +1,2058 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#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, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 0, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 0, 0x19, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Right DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 8, 0x19, 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;
+ int ret;
+
+ 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);
+ }
+
+ clk_disable_unprepare(nau8824->mclk);
+ } else {
+ dev_dbg(nau8824->dev, "system clock control : POWER ON\n");
+
+ ret = clk_prepare_enable(nau8824->mclk);
+ if (ret)
+ return ret;
+
+ /* 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;
+ unsigned int freq;
+
+ freq = clk_get_rate(nau8824->mclk);
+ if (!freq)
+ freq = nau8824->fs * 256;
+
+ /* 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 (freq / (0x1 << src) <= DMIC_CLK)
+ break;
+ }
+ dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, freq);
+ 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_CBP_CFP:
+ ctrl2_val |= NAU8824_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 |= 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;
+
+ nau8824->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(nau8824->mclk))
+ return PTR_ERR(nau8824->mclk);
+
+ 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" },
+ { }
+};
+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 <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
new file mode 100644
index 000000000000..d8e19515133c
--- /dev/null
+++ b/sound/soc/codecs/nau8824.h
@@ -0,0 +1,478 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@nuvoton.com>
+ */
+
+#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;
+ struct clk *mclk;
+ 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 000000000000..25b8b19e27ec
--- /dev/null
+++ b/sound/soc/codecs/nau8825.c
@@ -0,0 +1,2972 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ * Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ * Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/int_log.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#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_CBP_CFP:
+ ctrl2_val |= NAU8825_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 |= 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_optional(dev, "mclk");
+ if (IS_ERR(nau8825->mclk))
+ return PTR_ERR(nau8825->mclk);
+ if (!nau8825->mclk)
+ /* The MCLK is managed externally or not used at all */
+ dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+
+ 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" },
+ { }
+};
+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 <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644
index 000000000000..2abfbb5184da
--- /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 <anatol.pomozov@chrominium.org>
+ */
+
+#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/ntp8835.c b/sound/soc/codecs/ntp8835.c
new file mode 100644
index 000000000000..2cc4c6395f55
--- /dev/null
+++ b/sound/soc/codecs/ntp8835.c
@@ -0,0 +1,480 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8835/NTP8835C Audio Amplifiers
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/bits.h>
+#include <linux/reset.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8835_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8835_INPUT_FMT 0x0
+#define NTP8835_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8835_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8835_GSA_FMT 0x1
+#define NTP8835_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8835_GSA_BS(x) ((x) << 2)
+#define NTP8835_GSA_RIGHT_J BIT(0)
+#define NTP8835_GSA_LSB BIT(1)
+#define NTP8835_MCLK_FREQ_CTRL 0x2
+#define NTP8835_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8835_SOFT_MUTE 0x26
+#define NTP8835_SOFT_MUTE_SM1 BIT(0)
+#define NTP8835_SOFT_MUTE_SM2 BIT(1)
+#define NTP8835_SOFT_MUTE_SM3 BIT(2)
+#define NTP8835_PWM_SWITCH 0x27
+#define NTP8835_PWM_SWITCH_POF1 BIT(0)
+#define NTP8835_PWM_SWITCH_POF2 BIT(1)
+#define NTP8835_PWM_SWITCH_POF3 BIT(2)
+#define NTP8835_PWM_MASK_CTRL0 0x28
+#define NTP8835_PWM_MASK_CTRL0_OUT_LOW BIT(1)
+#define NTP8835_PWM_MASK_CTRL0_FPMLD BIT(2)
+#define NTP8835_MASTER_VOL 0x2e
+#define NTP8835_CHNL_A_VOL 0x2f
+#define NTP8835_CHNL_B_VOL 0x30
+#define NTP8835_CHNL_C_VOL 0x31
+#define REG_MAX NTP8835_CHNL_C_VOL
+
+#define NTP8835_FW_NAME "eq_8835.bin"
+#define NTP8835_FW_MAGIC 0x38383335 /* "8835" */
+
+struct ntp8835_priv {
+ struct i2c_client *i2c;
+ struct reset_control *reset;
+ unsigned int format;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+};
+
+static const DECLARE_TLV_DB_RANGE(ntp8835_vol_scale,
+ 0, 1, TLV_DB_SCALE_ITEM(-15000, 0, 0),
+ 2, 6, TLV_DB_SCALE_ITEM(-15000, 1000, 0),
+ 7, 0xff, TLV_DB_SCALE_ITEM(-10000, 50, 0),
+);
+
+static int ntp8835_mute_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->access =
+ (SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE);
+ uinfo->count = 1;
+
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int ntp8835_mute_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, NTP8835_SOFT_MUTE);
+
+ ucontrol->value.integer.value[0] = val ? 0 : 1;
+ return 0;
+}
+
+static int ntp8835_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ unsigned int val;
+
+ val = ucontrol->value.integer.value[0] ? 0 : 7;
+
+ snd_soc_component_write(component, NTP8835_SOFT_MUTE, val);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new ntp8835_vol_control[] = {
+ SOC_SINGLE_TLV("Playback Volume", NTP8835_MASTER_VOL, 0,
+ 0xff, 0, ntp8835_vol_scale),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Playback Switch",
+ .info = ntp8835_mute_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .get = ntp8835_mute_get,
+ .put = ntp8835_mute_put,
+ },
+};
+
+static void ntp8835_reset_gpio(struct ntp8835_priv *ntp8835)
+{
+ /*
+ * Proper initialization sequence for NTP835 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8835->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8835->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8835_sound_on[] = {
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_FPMLD },
+ { NTP8835_PWM_SWITCH, 0x00 },
+ { NTP8835_SOFT_MUTE, 0x00 },
+};
+
+static const struct reg_sequence ntp8835_sound_off[] = {
+ { NTP8835_SOFT_MUTE, NTP8835_SOFT_MUTE_SM1 |
+ NTP8835_SOFT_MUTE_SM2 |
+ NTP8835_SOFT_MUTE_SM3 },
+
+ { NTP8835_PWM_SWITCH, NTP8835_PWM_SWITCH_POF1 |
+ NTP8835_PWM_SWITCH_POF2 |
+ NTP8835_PWM_SWITCH_POF3 },
+
+ { NTP8835_PWM_MASK_CTRL0, NTP8835_PWM_MASK_CTRL0_OUT_LOW |
+ NTP8835_PWM_MASK_CTRL0_FPMLD },
+};
+
+static int ntp8835_load_firmware(struct ntp8835_priv *ntp8835)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8835->i2c, NTP8835_FW_NAME, NTP8835_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8835->i2c->dev,
+ "Could not find firmware %s\n", NTP8835_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8835_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_off,
+ ARRAY_SIZE(ntp8835_sound_off));
+
+ /*
+ * According to NTP8835 datasheet, 6.2 Timing Sequence (recommended):
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8835->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8835->mclk);
+
+ return 0;
+}
+
+static int ntp8835_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ntp8835_reset_gpio(ntp8835);
+ ret = clk_prepare_enable(ntp8835->mclk);
+ if (ret)
+ return ret;
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8835_sound_on,
+ ARRAY_SIZE(ntp8835_sound_on));
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret) {
+ dev_err(&ntp8835->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8835_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8835_vol_control,
+ ARRAY_SIZE(ntp8835_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8835_load_firmware(ntp8835);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8835_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route ntp8835_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+ { "OUT3", NULL, "AIFIN" },
+};
+
+static int ntp8835_set_component_sysclk(struct snd_soc_component *component,
+ int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (freq) {
+ case 12288000:
+ case 24576000:
+ case 18432000:
+ ntp8835->mclk_rate = freq;
+ break;
+ default:
+ ntp8835->mclk_rate = 0;
+ dev_err(component->dev, "Unsupported MCLK value: %u", freq);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_ntp8835 = {
+ .probe = ntp8835_probe,
+ .suspend = ntp8835_snd_suspend,
+ .resume = ntp8835_snd_resume,
+ .dapm_widgets = ntp8835_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8835_dapm_widgets),
+ .dapm_routes = ntp8835_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8835_dapm_routes),
+ .set_sysclk = ntp8835_set_component_sysclk,
+};
+
+static int ntp8835_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 ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int ret;
+
+ switch (ntp8835->mclk_rate) {
+ case 12288000:
+ mcf = 0;
+ break;
+ case 24576000:
+ mcf = 1;
+ break;
+ case 18432000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_MCLK_FREQ_CTRL,
+ NTP8835_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8835->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8835_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8835_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8835_INPUT_FMT,
+ NTP8835_INPUT_FMT_MASTER_MODE |
+ NTP8835_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8835_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8835_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8835_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8835_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8835_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8835_GSA_BS_MASK |
+ NTP8835_GSA_RIGHT_J |
+ NTP8835_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8835_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8835_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8835_priv *ntp8835 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8835->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+};
+
+static const struct snd_soc_dai_ops ntp8835_dai_ops = {
+ .hw_params = ntp8835_hw_params,
+ .set_fmt = ntp8835_set_fmt,
+};
+
+static struct snd_soc_dai_driver ntp8835_dai = {
+ .name = "ntp8835-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 3,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = NTP8835_FORMATS,
+ },
+ .ops = &ntp8835_dai_ops,
+};
+
+static const struct regmap_config ntp8835_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8835_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8835_priv *ntp8835;
+ struct regmap *regmap;
+ int ret;
+
+ ntp8835 = devm_kzalloc(&i2c->dev, sizeof(*ntp8835), GFP_KERNEL);
+ if (!ntp8835)
+ return -ENOMEM;
+
+ ntp8835->i2c = i2c;
+
+ ntp8835->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8835->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->reset),
+ "Failed to get reset\n");
+
+ ret = reset_control_deassert(ntp8835->reset);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to deassert reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8835);
+
+ ntp8835_reset_gpio(ntp8835);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8835_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8835,
+ &ntp8835_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8835->mclk = devm_clk_get_enabled(&i2c->dev, "mclk");
+ if (IS_ERR(ntp8835->mclk))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8835->mclk), "failed to get mclk\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8835_i2c_id[] = {
+ { "ntp8835" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);
+
+static const struct of_device_id ntp8835_of_match[] = {
+ {.compatible = "neofidelity,ntp8835",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8835_of_match);
+
+static struct i2c_driver ntp8835_i2c_driver = {
+ .probe = ntp8835_i2c_probe,
+ .id_table = ntp8835_i2c_id,
+ .driver = {
+ .name = "ntp8835",
+ .of_match_table = ntp8835_of_match,
+ },
+};
+module_i2c_driver(ntp8835_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8835 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c
new file mode 100644
index 000000000000..5593d48ef696
--- /dev/null
+++ b/sound/soc/codecs/ntp8918.c
@@ -0,0 +1,396 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for the NTP8918 Audio Amplifier
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ *
+ * Author: Igor Prusov <ivprusov@salutedevices.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/reset.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include <sound/initval.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/tlv.h>
+
+#include "ntpfw.h"
+
+#define NTP8918_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+
+#define NTP8918_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define NTP8918_INPUT_FMT 0x0
+#define NTP8918_INPUT_FMT_MASTER_MODE BIT(0)
+#define NTP8918_INPUT_FMT_GSA_MODE BIT(1)
+#define NTP8918_GSA_FMT 0x1
+#define NTP8918_GSA_BS_MASK GENMASK(3, 2)
+#define NTP8918_GSA_BS(x) ((x) << 2)
+#define NTP8918_GSA_RIGHT_J BIT(0)
+#define NTP8918_GSA_LSB BIT(1)
+#define NTP8918_MCLK_FREQ_CTRL 0x2
+#define NTP8918_MCLK_FREQ_MCF GENMASK(1, 0)
+#define NTP8918_MASTER_VOL 0x0C
+#define NTP8918_CHNL_A_VOL 0x17
+#define NTP8918_CHNL_B_VOL 0x18
+#define NTP8918_SOFT_MUTE 0x33
+#define NTP8918_SOFT_MUTE_SM1 BIT(0)
+#define NTP8918_SOFT_MUTE_SM2 BIT(1)
+#define NTP8918_PWM_SWITCH 0x34
+#define NTP8918_PWM_MASK_CTRL0 0x35
+#define REG_MAX NTP8918_PWM_MASK_CTRL0
+
+#define NTP8918_FW_NAME "eq_8918.bin"
+#define NTP8918_FW_MAGIC 0x38393138 /* "8918" */
+
+struct ntp8918_priv {
+ struct i2c_client *i2c;
+ struct clk *bck;
+ struct reset_control *reset;
+ unsigned int format;
+};
+
+static const DECLARE_TLV_DB_SCALE(ntp8918_master_vol_scale, -12550, 50, 0);
+
+static const struct snd_kcontrol_new ntp8918_vol_control[] = {
+ SOC_SINGLE_RANGE_TLV("Playback Volume", NTP8918_MASTER_VOL, 0,
+ 0x04, 0xff, 0, ntp8918_master_vol_scale),
+ SOC_SINGLE("Playback Switch", NTP8918_PWM_MASK_CTRL0, 1, 1, 1),
+};
+
+static void ntp8918_reset_gpio(struct ntp8918_priv *ntp8918)
+{
+ /*
+ * Proper initialization sequence for NTP8918 amplifier requires driving
+ * /RESET signal low during power up for at least 0.1us. The sequence is,
+ * according to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * Deassert for T2 >= 1ms...
+ */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(1000);
+
+ /* ...Assert for T3 >= 0.1us... */
+ reset_control_assert(ntp8918->reset);
+ fsleep(1);
+
+ /* ...Deassert, and wait for T4 >= 0.5ms before sound on sequence. */
+ reset_control_deassert(ntp8918->reset);
+ fsleep(500);
+}
+
+static const struct reg_sequence ntp8918_sound_off[] = {
+ { NTP8918_MASTER_VOL, 0 },
+};
+
+static const struct reg_sequence ntp8918_sound_on[] = {
+ { NTP8918_MASTER_VOL, 0b11 },
+};
+
+static int ntp8918_load_firmware(struct ntp8918_priv *ntp8918)
+{
+ int ret;
+
+ ret = ntpfw_load(ntp8918->i2c, NTP8918_FW_NAME, NTP8918_FW_MAGIC);
+ if (ret == -ENOENT) {
+ dev_warn_once(&ntp8918->i2c->dev, "Could not find firmware %s\n",
+ NTP8918_FW_NAME);
+ return 0;
+ }
+
+ return ret;
+}
+
+static int ntp8918_snd_suspend(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(component->regmap, true);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_off,
+ ARRAY_SIZE(ntp8918_sound_off));
+
+ /*
+ * According to NTP8918 datasheet, 6.2 Timing Sequence 1:
+ * wait after sound off for T6 >= 0.5ms
+ */
+ fsleep(500);
+ reset_control_assert(ntp8918->reset);
+
+ regcache_mark_dirty(component->regmap);
+ clk_disable_unprepare(ntp8918->bck);
+
+ return 0;
+}
+
+static int ntp8918_snd_resume(struct snd_soc_component *component)
+{
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = clk_prepare_enable(ntp8918->bck);
+ if (ret)
+ return ret;
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap_multi_reg_write_bypassed(component->regmap,
+ ntp8918_sound_on,
+ ARRAY_SIZE(ntp8918_sound_on));
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret) {
+ dev_err(&ntp8918->i2c->dev, "Failed to load firmware\n");
+ return ret;
+ }
+
+ regcache_cache_only(component->regmap, false);
+ snd_soc_component_cache_sync(component);
+
+ return 0;
+}
+
+static int ntp8918_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+
+ ret = snd_soc_add_component_controls(component, ntp8918_vol_control,
+ ARRAY_SIZE(ntp8918_vol_control));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to add controls\n");
+
+ ret = ntp8918_load_firmware(ntp8918);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to load firmware\n");
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget ntp8918_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC("AIFIN", "Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+};
+
+static const struct snd_soc_dapm_route ntp8918_dapm_routes[] = {
+ { "OUT1", NULL, "AIFIN" },
+ { "OUT2", NULL, "AIFIN" },
+};
+
+static const struct snd_soc_component_driver soc_component_ntp8918 = {
+ .probe = ntp8918_probe,
+ .suspend = ntp8918_snd_suspend,
+ .resume = ntp8918_snd_resume,
+ .dapm_widgets = ntp8918_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ntp8918_dapm_widgets),
+ .dapm_routes = ntp8918_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ntp8918_dapm_routes),
+};
+
+static int ntp8918_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 ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+ unsigned int input_fmt = 0;
+ unsigned int gsa_fmt = 0;
+ unsigned int gsa_fmt_mask;
+ unsigned int mcf;
+ int bclk;
+ int ret;
+
+ bclk = snd_soc_params_to_bclk(params);
+ switch (bclk) {
+ case 3072000:
+ case 2822400:
+ mcf = 0;
+ break;
+ case 6144000:
+ mcf = 1;
+ break;
+ case 2048000:
+ mcf = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_MCLK_FREQ_CTRL,
+ NTP8918_MCLK_FREQ_MCF, mcf);
+ if (ret)
+ return ret;
+
+ switch (ntp8918->format) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ gsa_fmt |= NTP8918_GSA_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ input_fmt |= NTP8918_INPUT_FMT_GSA_MODE;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, NTP8918_INPUT_FMT,
+ NTP8918_INPUT_FMT_MASTER_MODE |
+ NTP8918_INPUT_FMT_GSA_MODE,
+ input_fmt);
+
+ if (!(input_fmt & NTP8918_INPUT_FMT_GSA_MODE) || ret < 0)
+ return ret;
+
+ switch (params_width(params)) {
+ case 24:
+ gsa_fmt |= NTP8918_GSA_BS(0);
+ break;
+ case 20:
+ gsa_fmt |= NTP8918_GSA_BS(1);
+ break;
+ case 18:
+ gsa_fmt |= NTP8918_GSA_BS(2);
+ break;
+ case 16:
+ gsa_fmt |= NTP8918_GSA_BS(3);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ gsa_fmt_mask = NTP8918_GSA_BS_MASK |
+ NTP8918_GSA_RIGHT_J |
+ NTP8918_GSA_LSB;
+ return snd_soc_component_update_bits(component, NTP8918_GSA_FMT,
+ gsa_fmt_mask, gsa_fmt);
+}
+
+static int ntp8918_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct ntp8918_priv *ntp8918 = snd_soc_component_get_drvdata(component);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ case SND_SOC_DAIFMT_LEFT_J:
+ ntp8918->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ unsigned int mute_mask = NTP8918_SOFT_MUTE_SM1 |
+ NTP8918_SOFT_MUTE_SM2;
+
+ return snd_soc_component_update_bits(dai->component, NTP8918_SOFT_MUTE,
+ mute_mask, mute ? mute_mask : 0);
+}
+
+static const struct snd_soc_dai_ops ntp8918_dai_ops = {
+ .hw_params = ntp8918_hw_params,
+ .set_fmt = ntp8918_set_fmt,
+ .mute_stream = ntp8918_digital_mute,
+};
+
+static struct snd_soc_dai_driver ntp8918_dai = {
+ .name = "ntp8918-amplifier",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NTP8918_RATES,
+ .formats = NTP8918_FORMATS,
+ },
+ .ops = &ntp8918_dai_ops,
+};
+
+static const struct regmap_config ntp8918_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int ntp8918_i2c_probe(struct i2c_client *i2c)
+{
+ struct ntp8918_priv *ntp8918;
+ int ret;
+ struct regmap *regmap;
+
+ ntp8918 = devm_kzalloc(&i2c->dev, sizeof(*ntp8918), GFP_KERNEL);
+ if (!ntp8918)
+ return -ENOMEM;
+
+ ntp8918->i2c = i2c;
+
+ ntp8918->reset = devm_reset_control_get_shared(&i2c->dev, NULL);
+ if (IS_ERR(ntp8918->reset))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->reset), "Failed to get reset\n");
+
+ dev_set_drvdata(&i2c->dev, ntp8918);
+
+ ntp8918_reset_gpio(ntp8918);
+
+ regmap = devm_regmap_init_i2c(i2c, &ntp8918_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&i2c->dev, PTR_ERR(regmap),
+ "Failed to allocate regmap\n");
+
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_ntp8918,
+ &ntp8918_dai, 1);
+ if (ret)
+ return dev_err_probe(&i2c->dev, ret,
+ "Failed to register component\n");
+
+ ntp8918->bck = devm_clk_get_enabled(&i2c->dev, "bck");
+ if (IS_ERR(ntp8918->bck))
+ return dev_err_probe(&i2c->dev, PTR_ERR(ntp8918->bck), "failed to get bck clock\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id ntp8918_i2c_id[] = {
+ { "ntp8918" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
+
+static const struct of_device_id ntp8918_of_match[] = {
+ {.compatible = "neofidelity,ntp8918"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, ntp8918_of_match);
+
+static struct i2c_driver ntp8918_i2c_driver = {
+ .probe = ntp8918_i2c_probe,
+ .id_table = ntp8918_i2c_id,
+ .driver = {
+ .name = "ntp8918",
+ .of_match_table = ntp8918_of_match,
+ },
+};
+module_i2c_driver(ntp8918_i2c_driver);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("NTP8918 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.c b/sound/soc/codecs/ntpfw.c
new file mode 100644
index 000000000000..5ced2e966ab7
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ntpfw.c - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include "ntpfw.h"
+
+struct ntpfw_chunk {
+ __be16 length;
+ u8 step;
+ u8 data[];
+} __packed;
+
+struct ntpfw_header {
+ __be32 magic;
+} __packed;
+
+static bool ntpfw_verify(struct device *dev, const u8 *buf, size_t buf_size, u32 magic)
+{
+ const struct ntpfw_header *header = (struct ntpfw_header *)buf;
+ u32 buf_magic;
+
+ if (buf_size <= sizeof(*header)) {
+ dev_err(dev, "Failed to load firmware: image too small\n");
+ return false;
+ }
+
+ buf_magic = be32_to_cpu(header->magic);
+ if (buf_magic != magic) {
+ dev_err(dev, "Failed to load firmware: invalid magic 0x%x:\n", buf_magic);
+ return false;
+ }
+
+ return true;
+}
+
+static bool ntpfw_verify_chunk(struct device *dev, const struct ntpfw_chunk *chunk, size_t buf_size)
+{
+ size_t chunk_size;
+
+ if (buf_size <= sizeof(*chunk)) {
+ dev_err(dev, "Failed to load firmware: chunk size too big\n");
+ return false;
+ }
+
+ if (chunk->step != 2 && chunk->step != 5) {
+ dev_err(dev, "Failed to load firmware: invalid chunk step: %d\n", chunk->step);
+ return false;
+ }
+
+ chunk_size = be16_to_cpu(chunk->length);
+ if (chunk_size > buf_size) {
+ dev_err(dev, "Failed to load firmware: invalid chunk length\n");
+ return false;
+ }
+
+ if (chunk_size % chunk->step) {
+ dev_err(dev, "Failed to load firmware: chunk length and step mismatch\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int ntpfw_send_chunk(struct i2c_client *i2c, const struct ntpfw_chunk *chunk)
+{
+ int ret;
+ size_t i;
+ size_t length = be16_to_cpu(chunk->length);
+
+ for (i = 0; i < length; i += chunk->step) {
+ ret = i2c_master_send(i2c, &chunk->data[i], chunk->step);
+ if (ret != chunk->step) {
+ dev_err(&i2c->dev, "I2C send failed: %d\n", ret);
+ return ret < 0 ? ret : -EIO;
+ }
+ }
+
+ return 0;
+}
+
+int ntpfw_load(struct i2c_client *i2c, const char *name, u32 magic)
+{
+ struct device *dev = &i2c->dev;
+ const struct ntpfw_chunk *chunk;
+ const struct firmware *fw;
+ const u8 *data;
+ size_t leftover;
+ int ret;
+
+ ret = request_firmware(&fw, name, dev);
+ if (ret) {
+ dev_warn(dev, "request_firmware '%s' failed with %d\n",
+ name, ret);
+ return ret;
+ }
+
+ if (!ntpfw_verify(dev, fw->data, fw->size, magic)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ data = fw->data + sizeof(struct ntpfw_header);
+ leftover = fw->size - sizeof(struct ntpfw_header);
+
+ while (leftover) {
+ chunk = (struct ntpfw_chunk *)data;
+
+ if (!ntpfw_verify_chunk(dev, chunk, leftover)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ ret = ntpfw_send_chunk(i2c, chunk);
+ if (ret)
+ goto done;
+
+ data += be16_to_cpu(chunk->length) + sizeof(*chunk);
+ leftover -= be16_to_cpu(chunk->length) + sizeof(*chunk);
+ }
+
+done:
+ release_firmware(fw);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ntpfw_load);
+
+MODULE_AUTHOR("Igor Prusov <ivprusov@salutedevices.com>");
+MODULE_DESCRIPTION("Helper for loading Neofidelity amplifiers firmware");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ntpfw.h b/sound/soc/codecs/ntpfw.h
new file mode 100644
index 000000000000..1cf10d5480ee
--- /dev/null
+++ b/sound/soc/codecs/ntpfw.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/**
+ * ntpfw.h - Firmware helper functions for Neofidelity codecs
+ *
+ * Copyright (c) 2024, SaluteDevices. All Rights Reserved.
+ */
+
+#ifndef __NTPFW_H__
+#define __NTPFW_H__
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+
+/**
+ * ntpfw_load - load firmware to amplifier over i2c interface.
+ *
+ * @i2c Pointer to amplifier's I2C client.
+ * @name Firmware file name.
+ * @magic Magic number to validate firmware.
+ * @return 0 or error code upon error.
+ */
+int ntpfw_load(struct i2c_client *i2c, const char *name, const u32 magic);
+
+#endif /* __NTPFW_H__ */
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index a722a023c262..a1ec881d7084 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -1,29 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* PCM1681 ASoC codec driver
*
* Copyright (c) StreamUnlimited GmbH 2013
* Marek Belisko <marek.belisko@streamunlimited.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -73,7 +61,7 @@ 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 register reg)
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
{
return pcm1681_accessible_reg(dev, reg) &&
(reg != PCM1681_ZERO_DETECT_STATUS);
@@ -90,22 +78,27 @@ struct pcm1681_private {
static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
-static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+static int pcm1681_set_deemph(struct snd_soc_component *component)
{
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
- int i = 0, val = -1, enable = 0;
+ 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)
+ 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);
+ PCM1681_DEEMPH_RATE_MASK, val << 3);
enable = 1;
- } else
+ } else {
enable = 0;
+ }
/* enable/disable deemphasis functionality */
return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
@@ -115,10 +108,10 @@ static int pcm1681_set_deemph(struct snd_soc_codec *codec)
static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = priv->deemph;
+ ucontrol->value.integer.value[0] = priv->deemph;
return 0;
}
@@ -126,23 +119,23 @@ static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
- priv->deemph = ucontrol->value.enumerated.item[0];
+ priv->deemph = ucontrol->value.integer.value[0];
- return pcm1681_set_deemph(codec);
+ return pcm1681_set_deemph(component);
}
static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
- /* The PCM1681 can only be slave to all clocks */
- if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Invalid clocking mode\n");
+ /* 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;
}
@@ -151,10 +144,10 @@ static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+static int pcm1681_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm1681_private *priv = snd_soc_component_get_drvdata(component);
int val;
if (mute)
@@ -169,8 +162,8 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -195,7 +188,7 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
val = 0x05;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -203,13 +196,14 @@ static int pcm1681_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return pcm1681_set_deemph(codec);
+ 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,
- .digital_mute = pcm1681_digital_mute,
+ .mute_stream = pcm1681_mute,
+ .no_capture_mute = 1,
};
static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
@@ -283,23 +277,25 @@ static const struct regmap_config pcm1681_regmap = {
.readable_reg = pcm1681_accessible_reg,
};
-static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
+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},
+ {"pcm1681"},
{}
};
MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
-static int pcm1681_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int pcm1681_i2c_probe(struct i2c_client *client)
{
int ret;
struct pcm1681_private *priv;
@@ -317,25 +313,18 @@ static int pcm1681_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, priv);
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+ return devm_snd_soc_register_component(&client->dev,
+ &soc_component_dev_pcm1681,
&pcm1681_dai, 1);
}
-static int pcm1681_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static struct i2c_driver pcm1681_i2c_driver = {
.driver = {
.name = "pcm1681",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(pcm1681_dt_ids),
},
.id_table = pcm1681_i2c_id,
.probe = pcm1681_i2c_probe,
- .remove = pcm1681_i2c_remove,
};
module_i2c_driver(pcm1681_i2c_driver);
diff --git a/sound/soc/codecs/pcm1754.c b/sound/soc/codecs/pcm1754.c
new file mode 100644
index 000000000000..b68a528000be
--- /dev/null
+++ b/sound/soc/codecs/pcm1754.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * PCM1754 DAC ASoC codec driver
+ *
+ * Copyright (c) 2022 Alvin Šipraga <alsi@bang-olufsen.dk>
+ * Copyright (c) 2025 Stefan Kerkmann <s.kerkmann@pengutronix.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+struct pcm1754_priv {
+ unsigned int format;
+ struct gpio_desc *gpiod_mute;
+ struct gpio_desc *gpiod_format;
+};
+
+static int pcm1754_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int format)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+
+ priv->format = format;
+
+ return 0;
+}
+
+static int pcm1754_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 pcm1754_priv *priv = snd_soc_component_get_drvdata(component);
+ int format;
+
+ switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ format = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ switch (params_width(params)) {
+ case 16:
+ fallthrough;
+ case 24:
+ format = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ dev_err(component->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_format, format);
+
+ return 0;
+}
+
+static int pcm1754_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct pcm1754_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ gpiod_set_value_cansleep(priv->gpiod_mute, mute);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1754_dai_ops = {
+ .set_fmt = pcm1754_set_dai_fmt,
+ .hw_params = pcm1754_hw_params,
+ .mute_stream = pcm1754_mute_stream,
+};
+
+static const struct snd_soc_dai_driver pcm1754_dai = {
+ .name = "pcm1754",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ .rate_min = 5000,
+ .rate_max = 200000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE
+ },
+ .ops = &pcm1754_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget pcm1754_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 0, 0),
+
+ SND_SOC_DAPM_DAC("DAC1", "Channel 1 Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC2", "Channel 2 Playback", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_OUTPUT("VOUTL"),
+ SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm1754_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+
+ { "DAC1", NULL, "VCC" },
+ { "DAC2", NULL, "VCC" },
+
+ { "VOUTL", NULL, "DAC1" },
+ { "VOUTR", NULL, "DAC2" },
+};
+
+static const struct snd_soc_component_driver soc_component_dev_pcm1754 = {
+ .dapm_widgets = pcm1754_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1754_dapm_widgets),
+ .dapm_routes = pcm1754_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1754_dapm_routes),
+};
+
+static int pcm1754_probe(struct platform_device *pdev)
+{
+ struct pcm1754_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_dai_driver *dai_drv;
+ int ret;
+
+ dai_drv = devm_kmemdup(dev, &pcm1754_dai, sizeof(*dai_drv), GFP_KERNEL);
+ if (!dai_drv)
+ return -ENOMEM;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->gpiod_mute = devm_gpiod_get_optional(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->gpiod_format = devm_gpiod_get_optional(dev, "format", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_format))
+ return dev_err_probe(dev, PTR_ERR(priv->gpiod_format),
+ "failed to get format gpio");
+
+ dev_set_drvdata(dev, priv);
+
+ ret = devm_snd_soc_register_component(
+ &pdev->dev, &soc_component_dev_pcm1754, dai_drv, 1);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register");
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1754_of_match[] = {
+ { .compatible = "ti,pcm1754" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pcm1754_of_match);
+#endif
+
+static struct platform_driver pcm1754_codec_driver = {
+ .driver = {
+ .name = "pcm1754-codec",
+ .of_match_table = of_match_ptr(pcm1754_of_match),
+ },
+ .probe = pcm1754_probe,
+};
+
+module_platform_driver(pcm1754_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1754 driver");
+MODULE_AUTHOR("Alvin Šipraga <alsi@bang-olufsen.dk>");
+MODULE_AUTHOR("Stefan Kerkmann <s.kerkmann@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c
new file mode 100644
index 000000000000..abadf4f8ed5e
--- /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 <mylene.josserand@bootlin.com>
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#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" },
+ { }
+};
+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 <mylene.josserand@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.c b/sound/soc/codecs/pcm1789.c
new file mode 100644
index 000000000000..3ab381e9a856
--- /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 <mylene.josserand@bootlin.com>
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 <mylene.josserand@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1789.h b/sound/soc/codecs/pcm1789.h
new file mode 100644
index 000000000000..79439c8322b3
--- /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 <mylene.josserand@bootlin.com>
+
+#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/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
deleted file mode 100644
index 57b0c94a710b..000000000000
--- a/sound/soc/codecs/pcm1792a.c
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * PCM1792A ASoC codec driver
- *
- * Copyright (c) Amarula Solutions B.V. 2013
- *
- * Michael Trimarchi <michael@amarulasolutions.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/spi/spi.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-#include <sound/tlv.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#include "pcm1792a.h"
-
-#define PCM1792A_DAC_VOL_LEFT 0x10
-#define PCM1792A_DAC_VOL_RIGHT 0x11
-#define PCM1792A_FMT_CONTROL 0x12
-#define PCM1792A_MODE_CONTROL 0x13
-#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
-
-#define PCM1792A_FMT_MASK 0x70
-#define PCM1792A_FMT_SHIFT 4
-#define PCM1792A_MUTE_MASK 0x01
-#define PCM1792A_MUTE_SHIFT 0
-#define PCM1792A_ATLD_ENABLE (1 << 7)
-
-static const struct reg_default pcm1792a_reg_defaults[] = {
- { 0x10, 0xff },
- { 0x11, 0xff },
- { 0x12, 0x50 },
- { 0x13, 0x00 },
- { 0x14, 0x00 },
- { 0x15, 0x01 },
- { 0x16, 0x00 },
- { 0x17, 0x00 },
-};
-
-static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
-{
- return reg >= 0x10 && reg <= 0x17;
-}
-
-static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
-{
- bool accessible;
-
- accessible = pcm1792a_accessible_reg(dev, reg);
-
- return accessible && reg != 0x16 && reg != 0x17;
-}
-
-struct pcm1792a_private {
- struct regmap *regmap;
- unsigned int format;
- unsigned int rate;
-};
-
-static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int format)
-{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
-
- priv->format = format;
-
- return 0;
-}
-
-static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
- PCM1792A_MUTE_MASK, !!mute);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static int pcm1792a_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
- 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(codec->dev, "Invalid DAI format\n");
- return -EINVAL;
- }
-
- val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
-
- ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
- PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
- .set_fmt = pcm1792a_set_dai_fmt,
- .hw_params = pcm1792a_hw_params,
- .digital_mute = pcm1792a_digital_mute,
-};
-
-static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
-
-static const struct snd_kcontrol_new pcm1792a_controls[] = {
- SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
- PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
- pcm1792a_dac_tlv),
- SOC_SINGLE("DAC Invert Output Switch", PCM1792A_MODE_CONTROL, 7, 1, 0),
- SOC_SINGLE("DAC Rolloff Filter Switch", PCM1792A_MODE_CONTROL, 1, 1, 0),
-};
-
-static const struct snd_soc_dapm_widget pcm1792a_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 pcm1792a_dapm_routes[] = {
- { "IOUTL+", NULL, "Playback" },
- { "IOUTL-", NULL, "Playback" },
- { "IOUTR+", NULL, "Playback" },
- { "IOUTR-", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver pcm1792a_dai = {
- .name = "pcm1792a-hifi",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 2,
- .channels_max = 2,
- .rates = PCM1792A_RATES,
- .formats = PCM1792A_FORMATS, },
- .ops = &pcm1792a_dai_ops,
-};
-
-static const struct of_device_id pcm1792a_of_match[] = {
- { .compatible = "ti,pcm1792a", },
- { }
-};
-MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
-
-static const struct regmap_config pcm1792a_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = 23,
- .reg_defaults = pcm1792a_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(pcm1792a_reg_defaults),
- .writeable_reg = pcm1792a_writeable_reg,
- .readable_reg = pcm1792a_accessible_reg,
-};
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
- .controls = pcm1792a_controls,
- .num_controls = ARRAY_SIZE(pcm1792a_controls),
- .dapm_widgets = pcm1792a_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm1792a_dapm_widgets),
- .dapm_routes = pcm1792a_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm1792a_dapm_routes),
-};
-
-static int pcm1792a_spi_probe(struct spi_device *spi)
-{
- struct pcm1792a_private *pcm1792a;
- int ret;
-
- pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
- GFP_KERNEL);
- if (!pcm1792a)
- return -ENOMEM;
-
- spi_set_drvdata(spi, pcm1792a);
-
- pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
- if (IS_ERR(pcm1792a->regmap)) {
- ret = PTR_ERR(pcm1792a->regmap);
- dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
- return ret;
- }
-
- return snd_soc_register_codec(&spi->dev,
- &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
-}
-
-static int pcm1792a_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
-static const struct spi_device_id pcm1792a_spi_ids[] = {
- { "pcm1792a", 0 },
- { },
-};
-MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
-
-static struct spi_driver pcm1792a_codec_driver = {
- .driver = {
- .name = "pcm1792a",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(pcm1792a_of_match),
- },
- .id_table = pcm1792a_spi_ids,
- .probe = pcm1792a_spi_probe,
- .remove = pcm1792a_spi_remove,
-};
-
-module_spi_driver(pcm1792a_codec_driver);
-
-MODULE_DESCRIPTION("ASoC PCM1792A driver");
-MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h
deleted file mode 100644
index 51d5470fee16..000000000000
--- a/sound/soc/codecs/pcm1792a.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * definitions for PCM1792A
- *
- * Copyright 2013 Amarula Solutions
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#ifndef __PCM1792A_H__
-#define __PCM1792A_H__
-
-#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
- SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
- SNDRV_PCM_RATE_192000)
-
-#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S16_LE)
-
-#endif
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
new file mode 100644
index 000000000000..effc1dd6df22
--- /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 <jacob@teenage.engineering>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#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" },
+ { }
+};
+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 <jacob@teenage.engineering>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
new file mode 100644
index 000000000000..192fee90c971
--- /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 <michael@amarulasolutions.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#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 <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
new file mode 100644
index 000000000000..f52ff66b6e64
--- /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 <michael@amarulasolutions.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+
+#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 <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
new file mode 100644
index 000000000000..0039ca8ee742
--- /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 000000000000..a50f9f6e39c1
--- /dev/null
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -0,0 +1,60 @@
+// 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 <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#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 enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c);
+ 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 <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+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 000000000000..bc1b0f0698ed
--- /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 <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#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 <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+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 000000000000..13443f569ddb
--- /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 <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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 const 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 const 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 <dannenberg@ti.com>");
+MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
+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 000000000000..4d493754a3e2
--- /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 <dannenberg@ti.com>
+ * Andrew F. Davis <afd@ti.com>
+ */
+
+#ifndef _PCM186X_H_
+#define _PCM186X_H_
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+
+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
index 8fb445f33f6f..d3d2e7f40170 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ALSA Soc PCM3008 codec support
*
@@ -7,18 +8,13 @@
* Based on AC97 Soc codec, original copyright follow:
* Copyright 2005 Wolfson Microelectronics PLC.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Generic PCM3008 support.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <sound/core.h>
@@ -26,17 +22,22 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include "pcm3008.h"
+struct pcm3008 {
+ struct gpio_desc *dem0_pin;
+ struct gpio_desc *dem1_pin;
+ struct gpio_desc *pdad_pin;
+ struct gpio_desc *pdda_pin;
+};
static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdda_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdda_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -45,11 +46,11 @@ static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct pcm3008_setup_data *setup = codec->dev->platform_data;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pcm3008 *pcm = component->dev->platform_data;
- gpio_set_value_cansleep(setup->pdad_pin,
- SND_SOC_DAPM_EVENT_ON(event));
+ gpiod_set_value_cansleep(pcm->pdad_pin,
+ SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
@@ -98,20 +99,25 @@ static struct snd_soc_dai_driver pcm3008_dai = {
},
};
-static struct snd_soc_codec_driver soc_codec_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),
+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;
+ struct device *dev = &pdev->dev;
+ struct pcm3008 *pcm;
- if (!setup)
- return -EINVAL;
+ pcm = devm_kzalloc(dev, sizeof(*pcm), GFP_KERNEL);
+ if (!pcm)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, pcm);
/* DEM1 DEM0 DE-EMPHASIS_MODE
* Low Low De-emphasis 44.1 kHz ON
@@ -121,45 +127,33 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
*/
/* 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;
+ pcm->dem0_pin = devm_gpiod_get(dev, "dem0", GPIOD_OUT_HIGH);
+ if (IS_ERR(pcm->dem0_pin))
+ return PTR_ERR(pcm->dem0_pin);
/* 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;
+ pcm->dem1_pin = devm_gpiod_get(dev, "dem1", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->dem1_pin))
+ return PTR_ERR(pcm->dem1_pin);
/* Configure PDAD GPIO. */
- ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
- GPIOF_OUT_INIT_LOW, "codec_pdad");
- if (ret != 0)
- return ret;
+ pcm->pdad_pin = devm_gpiod_get(dev, "pdad", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdad_pin))
+ return PTR_ERR(pcm->pdad_pin);
/* 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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
-}
+ pcm->pdda_pin = devm_gpiod_get(dev, "pdda", GPIOD_OUT_LOW);
+ if (IS_ERR(pcm->pdda_pin))
+ return PTR_ERR(pcm->pdda_pin);
-static int pcm3008_codec_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
-
- return 0;
+ return devm_snd_soc_register_component(dev,
+ &soc_component_dev_pcm3008, &pcm3008_dai, 1);
}
MODULE_ALIAS("platform:pcm3008-codec");
static struct platform_driver pcm3008_codec_driver = {
.probe = pcm3008_codec_probe,
- .remove = pcm3008_codec_remove,
.driver = {
.name = "pcm3008-codec",
},
diff --git a/sound/soc/codecs/pcm3008.h b/sound/soc/codecs/pcm3008.h
deleted file mode 100644
index 7e5489ab4812..000000000000
--- a/sound/soc/codecs/pcm3008.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * PCM3008 ALSA SoC Layer
- *
- * Author: Hugo Villeneuve
- * Copyright (C) 2008 Lyrtech inc
- *
- * 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 __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 000000000000..3816b25a8ead
--- /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 <k.marinushkin@gmail.com>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#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 <k.marinushkin@gmail.com>");
+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 000000000000..6095841f2f56
--- /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 <k.marinushkin@gmail.com>
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#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 <k.marinushkin@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c
new file mode 100644
index 000000000000..8974200652e7
--- /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 <k.marinushkin@gmail.com>
+
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 <k.marinushkin@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h
new file mode 100644
index 000000000000..1b96835600b4
--- /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 <k.marinushkin@gmail.com>
+ */
+
+#ifndef _SND_SOC_PCM3060_H
+#define _SND_SOC_PCM3060_H
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+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 000000000000..ff18c74b616c
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM3168A codec i2c driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+
+#include <sound/soc.h>
+
+#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 acpi_device_id pcm3168a_acpi_match[] = {
+ { "PCM3168A" },
+ { "104C3168" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match);
+
+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",
+ .acpi_match_table = pcm3168a_acpi_match,
+ .of_match_table = pcm3168a_of_match,
+ .pm = pm_ptr(&pcm3168a_pm_ops),
+ },
+};
+module_i2c_driver(pcm3168a_i2c_driver);
+
+MODULE_DESCRIPTION("PCM3168A I2C codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+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 000000000000..0871338eacba
--- /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 <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <sound/soc.h>
+
+#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 = pm_ptr(&pcm3168a_pm_ops),
+ },
+};
+module_spi_driver(pcm3168a_spi_driver);
+
+MODULE_DESCRIPTION("PCM3168A SPI codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
new file mode 100644
index 000000000000..c8617a488b11
--- /dev/null
+++ b/sound/soc/codecs/pcm3168a.c
@@ -0,0 +1,914 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * PCM3168A codec driver
+ *
+ * Copyright (C) 2015 Imagination Technologies Ltd.
+ *
+ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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<<slots) || rx_mask >= (1<<slots)) {
+ dev_err(component->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 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 const 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_optional(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);
+ /* Fallback to the default if no clk entry available. */
+ if (!pcm3168a->sysclk)
+ pcm3168a->sysclk = 24576000;
+
+ 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);
+
+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;
+}
+
+EXPORT_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = {
+ RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+};
+
+MODULE_DESCRIPTION("PCM3168A codec driver");
+MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
new file mode 100644
index 000000000000..c4b7140dce39
--- /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 <Damien.Horsley@imgtec.com>
+ */
+
+#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 000000000000..9bca53de2475
--- /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 <florian.meier@koalo.de>
+ * Copyright 2013
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+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 const 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 <florian.meier@koalo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
index dcdfac0ffeb1..a1d849b0c50f 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -1,27 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include "pcm512x.h"
-static int pcm512x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int pcm512x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config = pcm512x_regmap;
@@ -37,10 +29,9 @@ static int pcm512x_i2c_probe(struct i2c_client *i2c,
return pcm512x_probe(&i2c->dev, regmap);
}
-static int pcm512x_i2c_remove(struct i2c_client *i2c)
+static void pcm512x_i2c_remove(struct i2c_client *i2c)
{
pcm512x_remove(&i2c->dev);
- return 0;
}
static const struct i2c_device_id pcm512x_i2c_id[] = {
@@ -48,33 +39,52 @@ static const struct i2c_device_id pcm512x_i2c_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
+ { "tas5754", },
+ { "tas5756", },
{ }
};
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", },
+ { .compatible = "ti,pcm5242", },
+ { .compatible = "ti,tas5754", },
+ { .compatible = "ti,tas5756", },
{ }
};
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,
+ .probe = pcm512x_i2c_probe,
.remove = pcm512x_i2c_remove,
.id_table = pcm512x_i2c_id,
.driver = {
.name = "pcm512x",
- .owner = THIS_MODULE,
- .of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .of_match_table = of_match_ptr(pcm512x_of_match),
+ .acpi_match_table = ACPI_PTR(pcm512x_acpi_match),
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
module_i2c_driver(pcm512x_i2c_driver);
MODULE_DESCRIPTION("ASoC PCM512x codec driver - I2C");
-MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c
index 7b64a9cef704..92f7f78a4e20 100644
--- a/sound/soc/codecs/pcm512x-spi.c
+++ b/sound/soc/codecs/pcm512x-spi.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
@@ -34,10 +26,9 @@ static int pcm512x_spi_probe(struct spi_device *spi)
return pcm512x_probe(&spi->dev, regmap);
}
-static int pcm512x_spi_remove(struct spi_device *spi)
+static void pcm512x_spi_remove(struct spi_device *spi)
{
pcm512x_remove(&spi->dev);
- return 0;
}
static const struct spi_device_id pcm512x_spi_id[] = {
@@ -45,6 +36,7 @@ static const struct spi_device_id pcm512x_spi_id[] = {
{ "pcm5122", },
{ "pcm5141", },
{ "pcm5142", },
+ { "pcm5242", },
{ },
};
MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
@@ -54,6 +46,7 @@ static const struct of_device_id pcm512x_of_match[] = {
{ .compatible = "ti,pcm5122", },
{ .compatible = "ti,pcm5141", },
{ .compatible = "ti,pcm5142", },
+ { .compatible = "ti,pcm5242", },
{ }
};
MODULE_DEVICE_TABLE(of, pcm512x_of_match);
@@ -64,10 +57,13 @@ static struct spi_driver pcm512x_spi_driver = {
.id_table = pcm512x_spi_id,
.driver = {
.name = "pcm512x",
- .owner = THIS_MODULE,
.of_match_table = pcm512x_of_match,
- .pm = &pcm512x_pm_ops,
+ .pm = pm_ptr(&pcm512x_pm_ops),
},
};
module_spi_driver(pcm512x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM512x codec driver - SPI");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index 9974f201a08f..007dfc0fa224 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -1,23 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/clk.h>
+#include <linux/kernel.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -29,11 +22,6 @@
#include "pcm512x.h"
-#define DIV_ROUND_DOWN_ULL(ll, d) \
- ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
-#define DIV_ROUND_CLOSEST_ULL(ll, d) \
- ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
-
#define PCM512x_NUM_SUPPLIES 3
static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = {
"AVDD",
@@ -54,6 +42,13 @@ struct pcm512x_priv {
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;
+ int force_pll_on;
};
/*
@@ -122,6 +117,8 @@ static const struct reg_default pcm512x_reg_defaults[] = {
{ 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)
@@ -224,6 +221,90 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg)
}
}
+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);
@@ -301,15 +382,77 @@ 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("Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
+SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL,
PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv),
-SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
+SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST,
PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv),
-SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT,
- PCM512x_RQMR_SHIFT, 1, 1),
+{
+ .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),
@@ -328,6 +471,13 @@ 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[] = {
@@ -346,6 +496,45 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
{ "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,
@@ -359,6 +548,7 @@ static const struct snd_pcm_hw_constraint_list constraints_slave = {
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;
@@ -377,7 +567,7 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
*/
memset(ranges, 0, sizeof(ranges));
ranges[0].min = 8000;
- ranges[0].max = 25000000 / frame_size / 2;
+ ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2;
ranges[1].min = DIV_ROUND_UP(16000000, frame_size);
ranges[1].max = 384000;
break;
@@ -392,8 +582,8 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params,
static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -408,7 +598,7 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
return snd_pcm_hw_rule_add(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
pcm512x_hw_rule_rate,
- NULL,
+ pcm512x,
SNDRV_PCM_HW_PARAM_FRAME_BITS,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
@@ -434,8 +624,8 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -460,15 +650,15 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- case SND_SOC_DAIFMT_CBM_CFS:
+ 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_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
return pcm512x_dai_startup_slave(substream, dai);
default:
@@ -476,10 +666,10 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream,
}
}
-static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
+static int pcm512x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct pcm512x_priv *pcm512x = dev_get_drvdata(codec->dev);
+ struct pcm512x_priv *pcm512x = dev_get_drvdata(component->dev);
int ret;
switch (level) {
@@ -491,7 +681,7 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, 0);
if (ret != 0) {
- dev_err(codec->dev, "Failed to remove standby: %d\n",
+ dev_err(component->dev, "Failed to remove standby: %d\n",
ret);
return ret;
}
@@ -501,15 +691,13 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_POWER,
PCM512x_RQST, PCM512x_RQST);
if (ret != 0) {
- dev_err(codec->dev, "Failed to request standby: %d\n",
+ dev_err(component->dev, "Failed to request standby: %d\n",
ret);
return ret;
}
break;
}
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -517,6 +705,8 @@ 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;
@@ -527,9 +717,10 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai,
* as many factors of 2 as possible, as that makes it easier
* to find a fast DAC rate
*/
- pow2 = 1 << fls((25000000 - 16000000) / bclk_rate);
+ pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate);
for (; pow2; pow2 >>= 1) {
- sck_rate = rounddown(25000000, bclk_rate * pow2);
+ sck_rate = rounddown(pcm512x_pll_max(pcm512x),
+ bclk_rate * pow2);
if (sck_rate >= 16000000)
break;
}
@@ -560,8 +751,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
unsigned long pll_rate)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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 */
@@ -576,8 +767,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai,
/* pllin_rate / P (or here, den) cannot be greater than 20 MHz */
if (pllin_rate / den > 20000000 && num < 8) {
- num *= 20000000 / (pllin_rate / den);
- den *= 20000000 / (pllin_rate / den);
+ 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);
@@ -667,8 +858,8 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
unsigned long osr_rate,
unsigned long pllin_rate)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -678,7 +869,7 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai,
return 0; /* futile, quit early */
/* run DAC no faster than 6144000 Hz */
- for (dac_rate = rounddown(6144000, osr_rate);
+ for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate);
dac_rate;
dac_rate -= osr_rate) {
@@ -698,8 +889,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params)
{
struct device *dev = dai->dev;
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -720,16 +911,21 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
int fssp;
int gpio;
- lrclk_div = snd_soc_params_to_frame_size(params);
- if (lrclk_div == 0) {
- dev_err(dev, "No LRCLK?\n");
- return -EINVAL;
+ 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_div = params->rate_den * 64 / lrclk_div;
- bclk_rate = DIV_ROUND_CLOSEST(sck_rate, bclk_div);
+ bclk_rate = params_rate(params) * lrclk_div;
+ bclk_div = DIV_ROUND_CLOSEST(sck_rate, bclk_rate);
mck_rate = sck_rate;
} else {
@@ -805,7 +1001,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
osr_rate = 16 * sample_rate;
/* run DSP no faster than 50 MHz */
- dsp_div = mck_rate > 50000000 ? 2 : 1;
+ dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1;
dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate);
if (dac_rate) {
@@ -818,7 +1014,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as dacref: %d\n", ret);
return ret;
}
@@ -827,7 +1023,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_DACIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as dacin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -836,7 +1032,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
dacsrc_rate = pllin_rate;
} else {
/* run DAC no faster than 6144000 Hz */
- unsigned long dac_mul = 6144000 / osr_rate;
+ unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000)
+ / osr_rate;
unsigned long sck_mul = sck_rate / osr_rate;
for (; dac_mul; dac_mul--) {
@@ -855,7 +1052,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_DAC_REF,
PCM512x_SDAC, PCM512x_SDAC_SCK);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set sck as dacref: %d\n", ret);
return ret;
}
@@ -863,28 +1060,30 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
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(dacsrc_rate / dac_div, 1536000);
- if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) {
+ 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(dacsrc_rate / dac_div, 2048000);
+ ncp_div = DIV_ROUND_UP(dac_rate, 2048000);
if (ncp_div > 128) {
dev_err(dev, "Failed to find NCP divider\n");
return -EINVAL;
}
}
- osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate);
- if (osr_div > 128) {
- dev_err(dev, "Failed to find OSR divider\n");
- return -EINVAL;
- }
-
idac = mck_rate / (dsp_div * sample_rate);
ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1);
@@ -937,29 +1136,29 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai,
return ret;
}
- if (sample_rate <= 48000)
+ if (sample_rate <= pcm512x_dac_max(pcm512x, 48000))
fssp = PCM512x_FSSP_48KHZ;
- else if (sample_rate <= 96000)
+ else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000))
fssp = PCM512x_FSSP_96KHZ;
- else if (sample_rate <= 192000)
+ 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(codec->dev, "Failed to set fs speed: %d\n", ret);
+ dev_err(component->dev, "Failed to set fs speed: %d\n", ret);
return ret;
}
- dev_dbg(codec->dev, "DSP divider %d\n", dsp_div);
- dev_dbg(codec->dev, "DAC divider %d\n", dac_div);
- dev_dbg(codec->dev, "NCP divider %d\n", ncp_div);
- dev_dbg(codec->dev, "OSR divider %d\n", osr_div);
- dev_dbg(codec->dev, "BCK divider %d\n", bclk_div);
- dev_dbg(codec->dev, "LRCK divider %d\n", lrclk_div);
- dev_dbg(codec->dev, "IDAC %d\n", idac);
- dev_dbg(codec->dev, "1<<FSSP %d\n", 1 << fssp);
+ 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<<FSSP %d\n", 1 << fssp);
return 0;
}
@@ -968,19 +1167,17 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component);
int alen;
int gpio;
- int clock_output;
- int master_mode;
int ret;
- dev_dbg(codec->dev, "hw_params %u Hz, %u channels\n",
+ dev_dbg(component->dev, "hw_params %u Hz, %u channels\n",
params_rate(params),
params_channels(params));
- switch (snd_pcm_format_width(params_format(params))) {
+ switch (params_width(params)) {
case 16:
alen = PCM512x_ALEN_16;
break;
@@ -994,62 +1191,41 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
alen = PCM512x_ALEN_32;
break;
default:
- dev_err(codec->dev, "Bad frame size: %d\n",
- snd_pcm_format_width(params_format(params)));
+ dev_err(component->dev, "Bad frame size: %d\n",
+ params_width(params));
return -EINVAL;
}
- switch (pcm512x->fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
- ret = regmap_update_bits(pcm512x->regmap,
- PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP
- | PCM512x_BCKO | PCM512x_LRKO,
- 0);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to enable slave mode: %d\n", ret);
- return ret;
- }
+ 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(codec->dev,
+ dev_err(component->dev,
"Failed to enable clock divider autoset: %d\n",
ret);
return ret;
}
- return 0;
- case SND_SOC_DAIFMT_CBM_CFM:
- clock_output = PCM512x_BCKO | PCM512x_LRKO;
- master_mode = PCM512x_RLRK | PCM512x_RBCK;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- clock_output = PCM512x_BCKO;
- master_mode = PCM512x_RBCK;
- break;
- default:
- return -EINVAL;
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_I2S_1,
- PCM512x_ALEN, alen);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to set frame size: %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(codec->dev, "Failed to set FLEX_A: %d\n", ret);
+ 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(codec->dev, "Failed to set FLEX_B: %d\n", ret);
+ dev_err(component->dev, "Failed to set FLEX_B: %d\n", ret);
return ret;
}
@@ -1062,7 +1238,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to ignore auto-clock failures: %d\n",
ret);
return ret;
@@ -1077,16 +1253,40 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
| PCM512x_IDSK | PCM512x_IDCH
| PCM512x_DCAS | PCM512x_IPLK);
if (ret != 0) {
- dev_err(codec->dev,
+ 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 (!pcm512x->force_pll_on) {
+ ret = regmap_update_bits(pcm512x->regmap,
+ PCM512x_PLL_EN, PCM512x_PLLE, 0);
+ } else {
+ /* provide minimum PLL config for TAS575x clocking
+ * and leave PLL enabled
+ */
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_0, 0x01);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_COEFF_1, 0x04);
+ if (ret != 0) {
+ dev_err(component->dev,
+ "Failed to set pll coefficient: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(pcm512x->regmap,
+ PCM512x_PLL_EN, 0x01);
+ dev_dbg(component->dev, "Enabling PLL for TAS575x\n");
+ }
+
if (ret != 0) {
- dev_err(codec->dev, "Failed to disable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to set pll mode: %d\n", ret);
return ret;
}
}
@@ -1099,7 +1299,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_REF,
PCM512x_SREF, PCM512x_SREF_GPIO);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio as pllref: %d\n", ret);
return ret;
}
@@ -1108,7 +1308,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_PLLIN,
PCM512x_GREF, gpio);
if (ret != 0) {
- dev_err(codec->dev,
+ dev_err(component->dev,
"Failed to set gpio %d as pllin: %d\n",
pcm512x->pll_in, ret);
return ret;
@@ -1117,33 +1317,15 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
PCM512x_PLLE, PCM512x_PLLE);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable pll: %d\n", ret);
+ dev_err(component->dev, "Failed to enable pll: %d\n", ret);
return ret;
}
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_BCLK_LRCLK_CFG,
- PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO,
- clock_output);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable clock output: %d\n", ret);
- return ret;
- }
-
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_MASTER_MODE,
- PCM512x_RLRK | PCM512x_RBCK,
- master_mode);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable master mode: %d\n", ret);
- return ret;
- }
- if (pcm512x->pll_out) {
gpio = PCM512x_G1OE << (pcm512x->pll_out - 1);
ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
gpio, gpio);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable gpio %d: %d\n",
+ dev_err(component->dev, "Failed to enable gpio %d: %d\n",
pcm512x->pll_out, ret);
return ret;
}
@@ -1152,62 +1334,181 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream,
ret = regmap_update_bits(pcm512x->regmap, gpio,
PCM512x_GxSL, PCM512x_GxSL_PLLCK);
if (ret != 0) {
- dev_err(codec->dev, "Failed to output pll on %d: %d\n",
+ dev_err(component->dev, "Failed to output pll on %d: %d\n",
ret, pcm512x->pll_out);
return ret;
}
-
- gpio = PCM512x_G1OE << (4 - 1);
- ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN,
- gpio, gpio);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to enable gpio %d: %d\n",
- 4, ret);
- return ret;
- }
-
- gpio = PCM512x_GPIO_OUTPUT_1 + 4 - 1;
- ret = regmap_update_bits(pcm512x->regmap, gpio,
- PCM512x_GxSL, PCM512x_GxSL_PLLLK);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to output pll lock on %d: %d\n",
- ret, 4);
- return ret;
- }
}
ret = regmap_update_bits(pcm512x->regmap, PCM512x_SYNCHRONIZE,
PCM512x_RQSY, PCM512x_RQSY_HALT);
if (ret != 0) {
- dev_err(codec->dev, "Failed to halt clocks: %d\n", ret);
+ 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(codec->dev, "Failed to resume clocks: %d\n", ret);
+ 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_codec *codec = dai->codec;
- struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec);
+ 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 = {
@@ -1226,16 +1527,16 @@ static struct snd_soc_dai_driver pcm512x_dai = {
.ops = &pcm512x_dai_ops,
};
-static struct snd_soc_codec_driver pcm512x_codec_driver = {
- .set_bias_level = pcm512x_set_bias_level,
- .idle_bias_off = true,
-
- .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),
+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 = {
@@ -1272,6 +1573,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (!pcm512x)
return -ENOMEM;
+ mutex_init(&pcm512x->mutex);
+
dev_set_drvdata(dev, pcm512x);
pcm512x->regmap = regmap;
@@ -1290,8 +1593,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
pcm512x->supply_nb[2].notifier_call = pcm512x_regulator_event_2;
for (i = 0; i < ARRAY_SIZE(pcm512x->supplies); i++) {
- ret = regulator_register_notifier(pcm512x->supplies[i].consumer,
- &pcm512x->supply_nb[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",
@@ -1321,13 +1625,15 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
}
pcm512x->sclk = devm_clk_get(dev, NULL);
- if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
+ 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);
- return ret;
+ goto err;
}
}
@@ -1353,7 +1659,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-in\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_in = val;
}
@@ -1362,7 +1668,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
if (val > 6) {
dev_err(dev, "Invalid pll-out\n");
ret = -EINVAL;
- goto err_clk;
+ goto err_pm;
}
pcm512x->pll_out = val;
}
@@ -1371,17 +1677,22 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
dev_err(dev,
"Error: both pll-in and pll-out, or none\n");
ret = -EINVAL;
- goto err_clk;
+ 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_clk;
+ goto err_pm;
}
+
+ if (!strcmp(np->name, "tas5756") ||
+ !strcmp(np->name, "tas5754"))
+ pcm512x->force_pll_on = 1;
+ dev_dbg(dev, "Device ID: %s\n", np->name);
}
#endif
- ret = snd_soc_register_codec(dev, &pcm512x_codec_driver,
+ 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);
@@ -1406,7 +1717,6 @@ void pcm512x_remove(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
- snd_soc_unregister_codec(dev);
pm_runtime_disable(dev);
if (!IS_ERR(pcm512x->sclk))
clk_disable_unprepare(pcm512x->sclk);
@@ -1415,7 +1725,6 @@ void pcm512x_remove(struct device *dev)
}
EXPORT_SYMBOL_GPL(pcm512x_remove);
-#ifdef CONFIG_PM
static int pcm512x_suspend(struct device *dev)
{
struct pcm512x_priv *pcm512x = dev_get_drvdata(dev);
@@ -1477,13 +1786,11 @@ static int pcm512x_resume(struct device *dev)
return 0;
}
-#endif
-const struct dev_pm_ops pcm512x_pm_ops = {
- SET_RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
+EXPORT_GPL_DEV_PM_OPS(pcm512x_pm_ops) = {
+ RUNTIME_PM_OPS(pcm512x_suspend, pcm512x_resume, NULL)
};
-EXPORT_SYMBOL_GPL(pcm512x_pm_ops);
MODULE_DESCRIPTION("ASoC PCM512x codec driver");
-MODULE_AUTHOR("Mark Brown <broonie@linaro.org>");
+MODULE_AUTHOR("Mark Brown <broonie@kernel.org>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h
index b7c310207223..08d04f539805 100644
--- a/sound/soc/codecs/pcm512x.h
+++ b/sound/soc/codecs/pcm512x.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Driver for the PCM512x CODECs
*
- * Author: Mark Brown <broonie@linaro.org>
+ * Author: Mark Brown <broonie@kernel.org>
* Copyright 2014 Linaro Ltd
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#ifndef _SND_SOC_PCM512X
@@ -112,7 +104,9 @@
#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 */
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
new file mode 100644
index 000000000000..08cc52b374a9
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.c
@@ -0,0 +1,2170 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC Device
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#include <linux/unaligned.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "pcm6240.h"
+
+static const struct i2c_device_id pcmdevice_i2c_id[] = {
+ { "adc3120", ADC3120 },
+ { "adc5120", ADC5120 },
+ { "adc6120", ADC6120 },
+ { "dix4192", DIX4192 },
+ { "pcm1690", PCM1690 },
+ { "pcm3120", PCM3120 },
+ { "pcm3140", PCM3140 },
+ { "pcm5120", PCM5120 },
+ { "pcm5140", PCM5140 },
+ { "pcm6120", PCM6120 },
+ { "pcm6140", PCM6140 },
+ { "pcm6240", PCM6240 },
+ { "pcm6260", PCM6260 },
+ { "pcm9211", PCM9211 },
+ { "pcmd3140", PCMD3140 },
+ { "pcmd3180", PCMD3180 },
+ { "pcmd512x", PCMD512X },
+ { "taa5212", TAA5212 },
+ { "taa5412", TAA5412 },
+ { "tad5212", TAD5212 },
+ { "tad5412", TAD5412 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id);
+
+static const char *const pcmdev_ctrl_name[] = {
+ "%s i2c%d Dev%d Ch%d Ana Volume",
+ "%s i2c%d Dev%d Ch%d Digi Volume",
+ "%s i2c%d Dev%d Ch%d Fine Volume",
+};
+
+static const struct pcmdevice_mixer_control adc5120_analog_gain_ctl[] = {
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH1_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ },
+ {
+ .shift = 1,
+ .reg = ADC5120_REG_CH2_ANALOG_GAIN,
+ .max = 0x54,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control adc5120_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = ADC5120_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm1690_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM1690_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6240_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6240_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6240_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_analog_gain_ctl[] = {
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH1_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH2_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH3_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH4_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH5_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ },
+ {
+ .shift = 2,
+ .reg = PCM6260_REG_CH6_ANALOG_GAIN,
+ .max = 0x42,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm6260_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM6260_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcm9211_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCM9211_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3140_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3140_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3140_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_digi_gain_ctl[] = {
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH1_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH2_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH3_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH4_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH5_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH6_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH7_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = PCMD3180_REG_CH8_DIGITAL_GAIN,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control pcmd3180_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH5_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH6_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH7_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = PCMD3180_REG_CH8_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_digi_vol_ctl[] = {
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH1_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH2_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH3_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_DIGITAL_VOLUME,
+ .max = 0xff,
+ .invert = 0,
+ }
+};
+
+static const struct pcmdevice_mixer_control taa5412_fine_gain_ctl[] = {
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH1_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH2_FINE_GAIN,
+ .max = 0xf,
+ .invert = 0,
+ },
+ {
+ .shift = 4,
+ .reg = TAA5412_REG_CH3_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ },
+ {
+ .shift = 0,
+ .reg = TAA5412_REG_CH4_FINE_GAIN,
+ .max = 0xf,
+ .invert = 4,
+ }
+};
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcmd3140_dig_gain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_fine_dig_gain_tlv,
+ -12750, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm1690_dig_gain_tlv,
+ -25500, 0);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm9211_dig_gain_tlv,
+ -11450, 2000);
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc5120_fgain_tlv,
+ -10050, 2700);
+static const DECLARE_TLV_DB_LINEAR(adc5120_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(pcm6260_fgain_tlv,
+ -10000, 2700);
+static const DECLARE_TLV_DB_LINEAR(pcm6260_chgain_tlv, 0, 4200);
+static const DECLARE_TLV_DB_MINMAX_MUTE(taa5412_dig_vol_tlv,
+ -8050, 4700);
+static const DECLARE_TLV_DB_LINEAR(taa5412_fine_gain_tlv,
+ -80, 70);
+
+static int pcmdev_change_dev(struct pcmdevice_priv *pcm_priv,
+ unsigned short dev_no)
+{
+ struct i2c_client *client = (struct i2c_client *)pcm_priv->client;
+ struct regmap *map = pcm_priv->regmap;
+ int ret;
+
+ if (client->addr == pcm_priv->addr[dev_no])
+ return 0;
+
+ client->addr = pcm_priv->addr[dev_no];
+ /* All pcmdevices share the same regmap, clear the page
+ * inside regmap once switching to another pcmdevice.
+ * Register 0 at any pages inside pcmdevice is the same
+ * one for page-switching.
+ */
+ ret = regmap_write(map, PCMDEVICE_PAGE_SELECT, 0);
+ if (ret < 0)
+ dev_err(pcm_priv->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_read(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int *val)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_read(map, reg, val);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_update_bits(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int mask,
+ unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(map, reg, mask, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err=%d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, ret;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int reg = mc->reg;
+ unsigned int val;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ if (pcm_dev->chip_id == PCM1690) {
+ ret = pcmdev_dev_read(pcm_dev, dev_no, PCM1690_REG_MODE_CTRL,
+ &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read mode err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ val &= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ /* Set to wide-range mode, before using vol ctrl. */
+ if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -25500;
+ goto out;
+ }
+ /* Set to fine mode, before using fine vol ctrl. */
+ if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) {
+ ucontrol->value.integer.value[0] = -12750;
+ goto out;
+ }
+ }
+
+ ret = pcmdev_dev_read(pcm_dev, dev_no, reg, &val);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: read err=%d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ val = (val >> shift) & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ ucontrol->value.integer.value[0] = val;
+out:
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_get_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_get_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static int pcmdev_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol, int vol_ctrl_type)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(component);
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+ int max = mc->max, rc;
+ unsigned int mask = BIT(fls(max)) - 1;
+ unsigned int dev_no = mc->dev_no;
+ unsigned int shift = mc->shift;
+ unsigned int val, val_mask;
+ unsigned int reg = mc->reg;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ val = ucontrol->value.integer.value[0] & mask;
+ val = (val > max) ? max : val;
+ val = mc->invert ? max - val : val;
+ val_mask = mask << shift;
+ val = val << shift;
+
+ switch (vol_ctrl_type) {
+ case PCMDEV_PCM1690_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE;
+ break;
+ case PCMDEV_PCM1690_FINE_VOL_CTRL:
+ val_mask |= PCM1690_REG_MODE_CTRL_DAMS_MSK;
+ val |= PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP;
+ break;
+ }
+
+ rc = pcmdev_dev_update_bits(pcm_dev, dev_no, reg, val_mask, val);
+ if (rc < 0)
+ dev_err(pcm_dev->dev, "%s: update_bits err = %d\n",
+ __func__, rc);
+ else
+ rc = 1;
+ mutex_unlock(&pcm_dev->codec_lock);
+ return rc;
+}
+
+static int pcmdevice_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_GENERIC_VOL_CTRL);
+}
+
+static int pcm1690_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol, PCMDEV_PCM1690_VOL_CTRL);
+}
+
+static int pcm1690_put_finevolsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return pcmdev_put_volsw(kcontrol, ucontrol,
+ PCMDEV_PCM1690_FINE_VOL_CTRL);
+}
+
+static const struct pcmdev_ctrl_info pcmdev_gain_ctl_info[][2] = {
+ // ADC3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // ADC6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // DIX4192
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // PCM1690
+ {
+ {
+ .gain = pcm1690_fine_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_volsw,
+ .put = pcm1690_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ {
+ .gain = pcm1690_dig_gain_tlv,
+ .pcmdev_ctrl = pcm1690_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm1690_digi_gain_ctl),
+ .get = pcm1690_get_finevolsw,
+ .put = pcm1690_put_finevolsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ },
+ // PCM3120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM3140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM5140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6120
+ {
+ {
+ .gain = adc5120_chgain_tlv,
+ .pcmdev_ctrl = adc5120_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = adc5120_fgain_tlv,
+ .pcmdev_ctrl = adc5120_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(adc5120_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6140
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6240
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6240_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6240_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6240_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM6260
+ {
+ {
+ .gain = pcm6260_chgain_tlv,
+ .pcmdev_ctrl = pcm6260_analog_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_analog_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 0,
+ },
+ {
+ .gain = pcm6260_fgain_tlv,
+ .pcmdev_ctrl = pcm6260_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm6260_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCM9211
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .gain = pcm9211_dig_gain_tlv,
+ .pcmdev_ctrl = pcm9211_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcm9211_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+
+ },
+ // PCMD3140
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3140_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3140_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD3180
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = pcmd3140_dig_gain_tlv,
+ .pcmdev_ctrl = pcmd3180_digi_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(pcmd3180_digi_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // PCMD512X
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAA5212
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAA5412
+ {
+ {
+ .gain = taa5412_fine_gain_tlv,
+ .pcmdev_ctrl = taa5412_fine_gain_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_fine_gain_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 2,
+ },
+ {
+ .gain = taa5412_dig_vol_tlv,
+ .pcmdev_ctrl = taa5412_digi_vol_ctl,
+ .ctrl_array_size = ARRAY_SIZE(taa5412_digi_vol_ctl),
+ .get = pcmdevice_get_volsw,
+ .put = pcmdevice_put_volsw,
+ .pcmdev_ctrl_name_id = 1,
+ },
+ },
+ // TAD5212
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+ // TAD5412
+ {
+ {
+ .ctrl_array_size = 0,
+ },
+ {
+ .ctrl_array_size = 0,
+ },
+ },
+};
+
+static int pcmdev_dev_bulk_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned char *data,
+ unsigned int len)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_write(map, reg, data, len);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: bulk_write err = %d\n", __func__,
+ ret);
+
+ return ret;
+}
+
+static int pcmdev_dev_write(struct pcmdevice_priv *pcm_dev,
+ unsigned int dev_no, unsigned int reg, unsigned int value)
+{
+ struct regmap *map = pcm_dev->regmap;
+ int ret;
+
+ if (dev_no >= pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: no such channel(%d)\n", __func__,
+ dev_no);
+ return -EINVAL;
+ }
+
+ ret = pcmdev_change_dev(pcm_dev, dev_no);
+ if (ret < 0) {
+ dev_err(pcm_dev->dev, "%s: chg dev err = %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = regmap_write(map, reg, value);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: err = %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int pcmdevice_info_profile(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ 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 = max(0, pcm_dev->regbin.ncfgs - 1);
+
+ return 0;
+}
+
+static int pcmdevice_get_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = pcm_dev->cur_conf;
+
+ return 0;
+}
+
+static int pcmdevice_set_profile_id(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec
+ = snd_soc_kcontrol_component(kcontrol);
+ struct pcmdevice_priv *pcm_dev =
+ snd_soc_component_get_drvdata(codec);
+ int nr_profile = ucontrol->value.integer.value[0];
+ int max = pcm_dev->regbin.ncfgs - 1;
+ int ret = 0;
+
+ nr_profile = clamp(nr_profile, 0, max);
+
+ if (pcm_dev->cur_conf != nr_profile) {
+ pcm_dev->cur_conf = nr_profile;
+ ret = 1;
+ }
+
+ return ret;
+}
+
+static int pcmdevice_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct pcmdevice_mixer_control *mc =
+ (struct pcmdevice_mixer_control *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = mc->max;
+ return 0;
+}
+
+static void pcm9211_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_update_bits(pcm_dev, i,
+ PCM9211_REG_SW_CTRL, PCM9211_REG_SW_CTRL_MRST_MSK,
+ PCM9211_REG_SW_CTRL_MRST);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static void pcmdevice_sw_rst(struct pcmdevice_priv *pcm_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ ret = pcmdev_dev_write(pcm_dev, i, PCMDEVICE_REG_SWRESET,
+ PCMDEVICE_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev %d swreset fail %d\n",
+ __func__, i, ret);
+ }
+}
+
+static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
+ const unsigned char *config_data, unsigned int config_size,
+ int *status)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_config_info *cfg_info;
+ struct pcmdevice_block_data **bk_da;
+ unsigned int config_offset = 0, i;
+
+ cfg_info = kzalloc(sizeof(struct pcmdevice_config_info), GFP_KERNEL);
+ if (!cfg_info) {
+ *status = -ENOMEM;
+ goto out;
+ }
+
+ if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) {
+ if (config_offset + 64 > (int)config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: cfg_name out of boundary\n", __func__);
+ goto out;
+ }
+ memcpy(cfg_info->cfg_name, &config_data[config_offset], 64);
+ config_offset += 64;
+ }
+
+ if (config_offset + 4 > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev, "%s: nblocks out of boundary\n",
+ __func__);
+ goto out;
+ }
+ cfg_info->nblocks =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks,
+ sizeof(struct pcmdevice_block_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(pcm_dev->dev,
+ "%s: out of boundary i = %d nblocks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+ bk_da[i] = kzalloc(sizeof(struct pcmdevice_block_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 == PCMDEVICE_BIN_BLK_PRE_POWER_UP) {
+ if (bk_da[i]->dev_idx == 0)
+ cfg_info->active_dev =
+ (1 << pcm_dev->ndev) - 1;
+ else
+ cfg_info->active_dev =
+ 1 << (bk_da[i]->dev_idx - 1);
+ }
+
+ bk_da[i]->yram_checksum =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_be32(&config_data[config_offset]);
+
+ config_offset += 4;
+
+ if (config_offset + bk_da[i]->block_size > config_size) {
+ *status = -EINVAL;
+ dev_err(pcm_dev->dev,
+ "%s: out of boundary: i = %d blks = %u\n",
+ __func__, i, cfg_info->nblocks);
+ break;
+ }
+
+ 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;
+}
+
+static int pcmdev_gain_ctrl_add(struct pcmdevice_priv *pcm_dev,
+ int dev_no, int ctl_id)
+{
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct pcmdevice_mixer_control *pcmdev_ctrl;
+ struct snd_kcontrol_new *pcmdev_controls;
+ int ret, mix_index = 0, name_id, chn;
+ unsigned int id = pcm_dev->chip_id;
+ const int nr_chn =
+ pcmdev_gain_ctl_info[id][ctl_id].ctrl_array_size;
+ const char *ctrl_name;
+ char *name;
+
+ if (!nr_chn) {
+ dev_dbg(pcm_dev->dev, "%s: no gain ctrl for %s\n", __func__,
+ pcm_dev->dev_name);
+ return 0;
+ }
+
+ pcmdev_controls = devm_kcalloc(pcm_dev->dev, nr_chn,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_controls)
+ return -ENOMEM;
+
+ name_id = pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl_name_id;
+
+ ctrl_name = pcmdev_ctrl_name[name_id];
+
+ for (chn = 1; chn <= nr_chn; chn++) {
+ name = devm_kzalloc(pcm_dev->dev,
+ SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ ctrl_name, pcm_dev->upper_dev_name, adap->nr,
+ dev_no, chn);
+ pcmdev_controls[mix_index].tlv.p =
+ pcmdev_gain_ctl_info[id][ctl_id].gain;
+ pcmdev_ctrl = devm_kmemdup(pcm_dev->dev,
+ &pcmdev_gain_ctl_info[id][ctl_id].pcmdev_ctrl[chn - 1],
+ sizeof(*pcmdev_ctrl), GFP_KERNEL);
+ if (!pcmdev_ctrl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ pcmdev_ctrl->dev_no = dev_no;
+ pcmdev_controls[mix_index].private_value =
+ (unsigned long)pcmdev_ctrl;
+ pcmdev_controls[mix_index].name = name;
+ pcmdev_controls[mix_index].access =
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ pcmdev_controls[mix_index].iface =
+ SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_controls[mix_index].info = pcmdevice_info_volsw;
+ pcmdev_controls[mix_index].get =
+ pcmdev_gain_ctl_info[id][ctl_id].get;
+ pcmdev_controls[mix_index].put =
+ pcmdev_gain_ctl_info[id][ctl_id].put;
+ mix_index++;
+ }
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_controls, mix_index);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+out:
+ return ret;
+}
+
+static int pcmdev_profile_ctrl_add(struct pcmdevice_priv *pcm_dev)
+{
+ struct snd_soc_component *comp = pcm_dev->component;
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ struct snd_kcontrol_new *pcmdev_ctrl;
+ char *name;
+ int ret;
+
+ pcmdev_ctrl = devm_kzalloc(pcm_dev->dev,
+ sizeof(struct snd_kcontrol_new), GFP_KERNEL);
+ if (!pcmdev_ctrl)
+ return -ENOMEM;
+
+ /* Create a mixer item for selecting the active profile */
+ name = devm_kzalloc(pcm_dev->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN,
+ "%s i2c%d Profile id", pcm_dev->upper_dev_name, adap->nr);
+ pcmdev_ctrl->name = name;
+ pcmdev_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ pcmdev_ctrl->info = pcmdevice_info_profile;
+ pcmdev_ctrl->get = pcmdevice_get_profile_id;
+ pcmdev_ctrl->put = pcmdevice_set_profile_id;
+
+ ret = snd_soc_add_component_controls(comp, pcmdev_ctrl, 1);
+ if (ret)
+ dev_err(pcm_dev->dev, "%s: add_controls err = %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static void pcmdevice_config_info_remove(void *ctxt)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *) ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ int i, j;
+
+ if (!cfg_info)
+ return;
+ for (i = 0; i < regbin->ncfgs; i++) {
+ if (!cfg_info[i])
+ continue;
+ if (cfg_info[i]->blk_data) {
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ if (!cfg_info[i]->blk_data[j])
+ continue;
+ kfree(cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
+ }
+ kfree(cfg_info[i]->blk_data);
+ }
+ kfree(cfg_info[i]);
+ }
+ kfree(cfg_info);
+}
+
+static int pcmdev_regbin_ready(const struct firmware *fmw, void *ctxt)
+{
+ struct pcmdevice_config_info **cfg_info;
+ struct pcmdevice_priv *pcm_dev = ctxt;
+ struct pcmdevice_regbin_hdr *fw_hdr;
+ struct pcmdevice_regbin *regbin;
+ unsigned int total_config_sz = 0;
+ int offset = 0, ret = 0, i;
+ unsigned char *buf;
+
+ regbin = &(pcm_dev->regbin);
+ fw_hdr = &(regbin->fw_hdr);
+ if (!fmw || !fmw->data) {
+ dev_err(pcm_dev->dev, "%s: failed to read %s\n",
+ __func__, pcm_dev->bin_name);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ buf = (unsigned char *)fmw->data;
+
+ fw_hdr->img_sz = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ if (fw_hdr->img_sz != fmw->size) {
+ dev_err(pcm_dev->dev, "%s: file size(%d) not match %u",
+ __func__, (int)fmw->size, fw_hdr->img_sz);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ fw_hdr->checksum = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_be32(&buf[offset]);
+ if (fw_hdr->binary_version_num < 0x103) {
+ dev_err(pcm_dev->dev, "%s: bin version 0x%04x is out of date",
+ __func__, fw_hdr->binary_version_num);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ offset += 4;
+ fw_hdr->drv_fw_version = get_unaligned_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 != pcm_dev->ndev) {
+ dev_err(pcm_dev->dev, "%s: invalid ndev(%u)\n", __func__,
+ fw_hdr->ndev);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (offset + PCMDEVICE_MAX_REGBIN_DEVICES > fw_hdr->img_sz) {
+ dev_err(pcm_dev->dev, "%s: devs out of boundary!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+
+ for (i = 0; i < PCMDEVICE_MAX_REGBIN_DEVICES; i++, offset++)
+ fw_hdr->devs[i] = buf[offset];
+
+ fw_hdr->nconfig = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < PCMDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_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(pcm_dev->dev, "%s: bin file error!\n", __func__);
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -EINVAL;
+ goto out;
+ }
+ cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL);
+ if (!cfg_info) {
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ ret = -ENOMEM;
+ goto out;
+ }
+ regbin->cfg_info = cfg_info;
+ regbin->ncfgs = 0;
+ for (i = 0; i < (int)fw_hdr->nconfig; i++) {
+ cfg_info[i] = pcmdevice_add_config(ctxt, &buf[offset],
+ fw_hdr->config_size[i], &ret);
+ if (ret) {
+ /* In case the bin file is partially destroyed. */
+ if (regbin->ncfgs == 0)
+ pcm_dev->fw_state = PCMDEVICE_FW_LOAD_FAILED;
+ break;
+ }
+ offset += (int)fw_hdr->config_size[i];
+ regbin->ncfgs += 1;
+ }
+
+out:
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev,
+ "%s: remove config due to fw load error!\n", __func__);
+ pcmdevice_config_info_remove(pcm_dev);
+ }
+
+ return ret;
+}
+
+static int pcmdevice_comp_probe(struct snd_soc_component *comp)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(comp);
+ struct i2c_adapter *adap = pcm_dev->client->adapter;
+ const struct firmware *fw_entry = NULL;
+ int ret, i, j;
+
+ mutex_lock(&pcm_dev->codec_lock);
+
+ pcm_dev->component = comp;
+
+ for (i = 0; i < pcm_dev->ndev; i++) {
+ for (j = 0; j < 2; j++) {
+ ret = pcmdev_gain_ctrl_add(pcm_dev, i, j);
+ if (ret < 0)
+ goto out;
+ }
+ }
+
+ if (comp->name_prefix) {
+ /* There's name_prefix defined in DTS. Bin file name will be
+ * name_prefix.bin stores the firmware including register
+ * setting and params for different filters inside chips, it
+ * must be copied into firmware folder. The same types of
+ * pcmdevices sitting on the same i2c bus will be aggregated as
+ * one single codec, all of them share the same bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s.bin", comp->name_prefix);
+ } else {
+ /* There's NO name_prefix defined in DTS. Bin file name will be
+ * device-name[defined in pcmdevice_i2c_id]-i2c-bus_id
+ * [0,1,...,N]-sum[1,...,4]dev.bin stores the firmware
+ * including register setting and params for different filters
+ * inside chips, it must be copied into firmware folder. The
+ * same types of pcmdevices sitting on the same i2c bus will be
+ * aggregated as one single codec, all of them share the same
+ * bin file.
+ */
+ scnprintf(pcm_dev->bin_name, PCMDEVICE_BIN_FILENAME_LEN,
+ "%s-i2c-%d-%udev.bin", pcm_dev->dev_name, adap->nr,
+ pcm_dev->ndev);
+ }
+
+ ret = request_firmware(&fw_entry, pcm_dev->bin_name, pcm_dev->dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: request %s err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+
+ ret = pcmdev_regbin_ready(fw_entry, pcm_dev);
+ if (ret) {
+ dev_err(pcm_dev->dev, "%s: %s parse err = %d\n", __func__,
+ pcm_dev->bin_name, ret);
+ goto out;
+ }
+ ret = pcmdev_profile_ctrl_add(pcm_dev);
+out:
+ release_firmware(fw_entry);
+
+ mutex_unlock(&pcm_dev->codec_lock);
+ return ret;
+}
+
+
+static void pcmdevice_comp_remove(struct snd_soc_component *codec)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+
+ if (!pcm_dev)
+ return;
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_config_info_remove(pcm_dev);
+ mutex_unlock(&pcm_dev->codec_lock);
+}
+
+static const struct snd_soc_dapm_widget pcmdevice_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI1 OUT", "ASI1 Capture",
+ 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("MIC"),
+};
+
+static const struct snd_soc_dapm_route pcmdevice_audio_map[] = {
+ {"OUT", NULL, "ASI"},
+ {"ASI1 OUT", NULL, "MIC"},
+};
+
+static const struct snd_soc_component_driver
+ soc_codec_driver_pcmdevice = {
+ .probe = pcmdevice_comp_probe,
+ .remove = pcmdevice_comp_remove,
+ .dapm_widgets = pcmdevice_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcmdevice_dapm_widgets),
+ .dapm_routes = pcmdevice_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pcmdevice_audio_map),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 0,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int pcmdev_single_byte_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int i, ret;
+
+ offset += 2;
+ if (offset + 4 * len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d byt wr out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < len; i++) {
+ ret = pcmdev_dev_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ data[offset + 3]);
+ /* skip this error for next operation or next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d single write err\n",
+ __func__, devn);
+
+ offset += 4;
+ }
+
+ return offset;
+}
+
+static int pcmdev_burst_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned short len = get_unaligned_be16(&data[2]);
+ int offset = 2;
+ int ret;
+
+ offset += 2;
+ if (offset + 4 + len > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d burst Out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ if (len % 4) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bst-len(%u) not div by 4\n",
+ __func__, devn, len);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_bulk_write(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 1], data[offset + 2]),
+ &(data[offset + 4]), len);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d bulk_write err = %d\n",
+ __func__, devn, ret);
+
+ offset += (len + 4);
+
+ return offset;
+}
+
+static int pcmdev_delay(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ unsigned int delay_time = 0;
+ int offset = 2;
+
+ if (offset + 2 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d delay out of boundary\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ delay_time = get_unaligned_be16(&data[2]) * 1000;
+ usleep_range(delay_time, delay_time + 50);
+ offset += 2;
+
+ return offset;
+}
+
+static int pcmdev_bits_wr(struct pcmdevice_priv *pcm_dev,
+ unsigned char *data, int devn, int sublocksize)
+{
+ int offset = 2;
+ int ret;
+
+ if (offset + 6 > sublocksize) {
+ dev_err(pcm_dev->dev, "%s: dev-%d bit write out of memory\n",
+ __func__, devn);
+ return -EINVAL;
+ }
+ ret = pcmdev_dev_update_bits(pcm_dev, devn,
+ PCMDEVICE_REG(data[offset + 3], data[offset + 4]),
+ data[offset + 1], data[offset + 5]);
+ /* skip this error for next devices */
+ if (ret < 0)
+ dev_err(pcm_dev->dev, "%s: dev-%d update_bits err = %d\n",
+ __func__, devn, ret);
+
+ offset += 6;
+
+ return offset;
+}
+
+static int pcmdevice_process_block(void *ctxt, unsigned char *data,
+ unsigned char dev_idx, int sublocksize)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ int devn, dev_end, ret = 0;
+ unsigned char subblk_typ = data[1];
+
+ if (dev_idx) {
+ devn = dev_idx - 1;
+ dev_end = dev_idx;
+ } else {
+ devn = 0;
+ dev_end = pcm_dev->ndev;
+ }
+
+ /* loop in case of several devices sharing the same sub-block */
+ for (; devn < dev_end; devn++) {
+ switch (subblk_typ) {
+ case PCMDEVICE_CMD_SING_W:
+ ret = pcmdev_single_byte_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_BURST:
+ ret = pcmdev_burst_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_DELAY:
+ ret = pcmdev_delay(pcm_dev, data, devn, sublocksize);
+ break;
+ case PCMDEVICE_CMD_FIELD_W:
+ ret = pcmdev_bits_wr(pcm_dev, data, devn, sublocksize);
+ break;
+ default:
+ break;
+ }
+ /*
+ * In case of sub-block error, break the loop for the rest of
+ * devices.
+ */
+ if (ret < 0)
+ break;
+ }
+
+ return ret;
+}
+
+static void pcmdevice_select_cfg_blk(void *ctxt, int conf_no,
+ unsigned char block_type)
+{
+ struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
+ struct pcmdevice_regbin *regbin = &(pcm_dev->regbin);
+ struct pcmdevice_config_info **cfg_info = regbin->cfg_info;
+ struct pcmdevice_block_data **blk_data;
+ int j, k;
+
+ if (conf_no >= regbin->ncfgs || conf_no < 0 || NULL == cfg_info) {
+ dev_err(pcm_dev->dev, "%s: conf_no should be less than %u\n",
+ __func__, regbin->ncfgs);
+ goto out;
+ }
+ blk_data = cfg_info[conf_no]->blk_data;
+
+ for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) {
+ unsigned int length = 0, ret;
+
+ if (block_type > 5 || block_type < 2) {
+ dev_err(pcm_dev->dev,
+ "%s: block_type should be out of range\n",
+ __func__);
+ goto out;
+ }
+ if (block_type != blk_data[j]->block_type)
+ continue;
+
+ for (k = 0; k < (int)blk_data[j]->n_subblks; k++) {
+ ret = pcmdevice_process_block(pcm_dev,
+ blk_data[j]->regdata + length,
+ blk_data[j]->dev_idx,
+ blk_data[j]->block_size - length);
+ length += ret;
+ if (blk_data[j]->block_size < length) {
+ dev_err(pcm_dev->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(pcm_dev->dev, "%s: %u %u size is not same\n",
+ __func__, length, blk_data[j]->block_size);
+ }
+
+out:
+ return;
+}
+
+static int pcmdevice_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *codec = dai->component;
+ struct pcmdevice_priv *pcm_dev = snd_soc_component_get_drvdata(codec);
+ unsigned char block_type;
+
+ if (pcm_dev->fw_state == PCMDEVICE_FW_LOAD_FAILED) {
+ dev_err(pcm_dev->dev, "%s: bin file not loaded\n", __func__);
+ return -EINVAL;
+ }
+
+ if (mute)
+ block_type = PCMDEVICE_BIN_BLK_PRE_SHUTDOWN;
+ else
+ block_type = PCMDEVICE_BIN_BLK_PRE_POWER_UP;
+
+ mutex_lock(&pcm_dev->codec_lock);
+ pcmdevice_select_cfg_blk(pcm_dev, pcm_dev->cur_conf, block_type);
+ mutex_unlock(&pcm_dev->codec_lock);
+ return 0;
+}
+
+static int pcmdevice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct pcmdevice_priv *pcm_dev = snd_soc_dai_get_drvdata(dai);
+ unsigned int fsrate;
+ unsigned int slot_width;
+ int bclk_rate;
+ int ret = 0;
+
+ fsrate = params_rate(params);
+ switch (fsrate) {
+ case 48000:
+ break;
+ case 44100:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect sample rate = %u\n",
+ __func__, fsrate);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ slot_width = params_width(params);
+ switch (slot_width) {
+ case 16:
+ break;
+ case 20:
+ break;
+ case 24:
+ break;
+ case 32:
+ break;
+ default:
+ dev_err(pcm_dev->dev, "%s: incorrect slot width = %u\n",
+ __func__, slot_width);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ bclk_rate = snd_soc_params_to_bclk(params);
+ if (bclk_rate < 0) {
+ dev_err(pcm_dev->dev, "%s: incorrect bclk rate = %d\n",
+ __func__, bclk_rate);
+ ret = bclk_rate;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_soc_dai_ops pcmdevice_dai_ops = {
+ .mute_stream = pcmdevice_mute,
+ .hw_params = pcmdevice_hw_params,
+};
+
+static struct snd_soc_dai_driver pcmdevice_dai_driver[] = {
+ {
+ .name = "pcmdevice-codec",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = PCMDEVICE_MAX_CHANNELS,
+ .rates = PCMDEVICE_RATES,
+ .formats = PCMDEVICE_FORMATS,
+ },
+ .ops = &pcmdevice_dai_ops,
+ .symmetric_rate = 1,
+ }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcmdevice_of_match[] = {
+ { .compatible = "ti,adc3120" },
+ { .compatible = "ti,adc5120" },
+ { .compatible = "ti,adc6120" },
+ { .compatible = "ti,dix4192" },
+ { .compatible = "ti,pcm1690" },
+ { .compatible = "ti,pcm3120" },
+ { .compatible = "ti,pcm3140" },
+ { .compatible = "ti,pcm5120" },
+ { .compatible = "ti,pcm5140" },
+ { .compatible = "ti,pcm6120" },
+ { .compatible = "ti,pcm6140" },
+ { .compatible = "ti,pcm6240" },
+ { .compatible = "ti,pcm6260" },
+ { .compatible = "ti,pcm9211" },
+ { .compatible = "ti,pcmd3140" },
+ { .compatible = "ti,pcmd3180" },
+ { .compatible = "ti,pcmd512x" },
+ { .compatible = "ti,taa5212" },
+ { .compatible = "ti,taa5412" },
+ { .compatible = "ti,tad5212" },
+ { .compatible = "ti,tad5412" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pcmdevice_of_match);
+#endif
+
+static const struct regmap_range_cfg pcmdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = PCMDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config pcmdevice_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .ranges = pcmdevice_ranges,
+ .num_ranges = ARRAY_SIZE(pcmdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev)
+{
+ if (pcm_dev->irq)
+ free_irq(pcm_dev->irq, pcm_dev);
+ mutex_destroy(&pcm_dev->codec_lock);
+}
+
+static char *str_to_upper(char *str)
+{
+ char *orig = str;
+
+ if (!str)
+ return NULL;
+
+ while (*str) {
+ *str = toupper(*str);
+ str++;
+ }
+
+ return orig;
+}
+
+static int pcmdevice_i2c_probe(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev;
+ struct device_node *np;
+ unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES];
+ int ret = 0, i = 0, ndev = 0;
+
+ pcm_dev = devm_kzalloc(&i2c->dev, sizeof(*pcm_dev), GFP_KERNEL);
+ if (!pcm_dev)
+ return -ENOMEM;
+
+ pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c);
+
+ pcm_dev->dev = &i2c->dev;
+ pcm_dev->client = i2c;
+
+ if (pcm_dev->chip_id >= MAX_DEVICE)
+ pcm_dev->chip_id = 0;
+
+ strscpy(pcm_dev->dev_name, pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->dev_name));
+
+ strscpy(pcm_dev->upper_dev_name,
+ pcmdevice_i2c_id[pcm_dev->chip_id].name,
+ sizeof(pcm_dev->upper_dev_name));
+
+ str_to_upper(pcm_dev->upper_dev_name);
+
+ pcm_dev->regmap = devm_regmap_init_i2c(i2c, &pcmdevice_i2c_regmap);
+ if (IS_ERR(pcm_dev->regmap)) {
+ ret = PTR_ERR(pcm_dev->regmap);
+ dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ i2c_set_clientdata(i2c, pcm_dev);
+ mutex_init(&pcm_dev->codec_lock);
+ np = pcm_dev->dev->of_node;
+
+ if (IS_ENABLED(CONFIG_OF)) {
+ u64 addr;
+
+ for (i = 0; i < PCMDEVICE_MAX_I2C_DEVICES; i++) {
+ if (of_property_read_reg(np, i, &addr, NULL))
+ break;
+ dev_addrs[ndev++] = addr;
+ }
+ } else {
+ ndev = 1;
+ dev_addrs[0] = i2c->addr;
+ }
+ pcm_dev->irq = of_irq_get(np, 0);
+
+ for (i = 0; i < ndev; i++)
+ pcm_dev->addr[i] = dev_addrs[i];
+
+ pcm_dev->ndev = ndev;
+
+ pcm_dev->hw_rst = devm_gpiod_get_optional(&i2c->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ /* No reset GPIO, no side-effect */
+ if (IS_ERR(pcm_dev->hw_rst)) {
+ if (pcm_dev->chip_id == PCM9211 || pcm_dev->chip_id == PCM1690)
+ pcm9211_sw_rst(pcm_dev);
+ else
+ pcmdevice_sw_rst(pcm_dev);
+ } else {
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(pcm_dev->hw_rst, 1);
+ }
+
+ if (pcm_dev->chip_id == PCM1690)
+ goto skip_interrupt;
+ if (pcm_dev->irq) {
+ dev_dbg(pcm_dev->dev, "irq = %d", pcm_dev->irq);
+ } else
+ dev_err(pcm_dev->dev, "No irq provided\n");
+
+skip_interrupt:
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_driver_pcmdevice, pcmdevice_dai_driver,
+ ARRAY_SIZE(pcmdevice_dai_driver));
+ if (ret < 0)
+ dev_err(&i2c->dev, "probe register comp failed %d\n", ret);
+
+out:
+ if (ret < 0)
+ pcmdevice_remove(pcm_dev);
+ return ret;
+}
+
+static void pcmdevice_i2c_remove(struct i2c_client *i2c)
+{
+ struct pcmdevice_priv *pcm_dev = i2c_get_clientdata(i2c);
+
+ pcmdevice_remove(pcm_dev);
+}
+
+static struct i2c_driver pcmdevice_i2c_driver = {
+ .driver = {
+ .name = "pcmdevice-codec",
+ .of_match_table = of_match_ptr(pcmdevice_of_match),
+ },
+ .probe = pcmdevice_i2c_probe,
+ .remove = pcmdevice_i2c_remove,
+ .id_table = pcmdevice_i2c_id,
+};
+module_i2c_driver(pcmdevice_i2c_driver);
+
+MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>");
+MODULE_DESCRIPTION("ASoC PCM6240 Family Audio ADC/DAC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h
new file mode 100644
index 000000000000..2d8f9e798139
--- /dev/null
+++ b/sound/soc/codecs/pcm6240.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+//
+// ALSA SoC Texas Instruments PCM6240 Family Audio ADC/DAC/Router
+//
+// Copyright (C) 2022 - 2024 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The PCM6240 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// PCM6240 Family Audio chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+//
+
+#ifndef __PCM6240_H__
+#define __PCM6240_H__
+
+enum pcm_device {
+ ADC3120,
+ ADC5120,
+ ADC6120,
+ DIX4192,
+ PCM1690,
+ PCM3120,
+ PCM3140,
+ PCM5120,
+ PCM5140,
+ PCM6120,
+ PCM6140,
+ PCM6240,
+ PCM6260,
+ PCM9211,
+ PCMD3140,
+ PCMD3180,
+ PCMD512X,
+ TAA5212,
+ TAA5412,
+ TAD5212,
+ TAD5412,
+ MAX_DEVICE,
+};
+
+#define PCMDEV_GENERIC_VOL_CTRL 0x0
+#define PCMDEV_PCM1690_VOL_CTRL 0x1
+#define PCMDEV_PCM1690_FINE_VOL_CTRL 0x2
+
+/* Maximum number of I2C addresses */
+#define PCMDEVICE_MAX_I2C_DEVICES 4
+/* Maximum number defined in REGBIN protocol */
+#define PCMDEVICE_MAX_REGBIN_DEVICES 8
+#define PCMDEVICE_CONFIG_SUM 64
+#define PCMDEVICE_BIN_FILENAME_LEN 64
+
+#define PCMDEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+#define PCMDEVICE_MAX_CHANNELS 8
+#define PCMDEVICE_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)
+
+/* PAGE Control Register (available in page0 of each book) */
+#define PCMDEVICE_PAGE_SELECT 0x00
+#define PCMDEVICE_REG(page, reg) ((page * 128) + reg)
+#define PCMDEVICE_REG_SWRESET PCMDEVICE_REG(0X0, 0x01)
+#define PCMDEVICE_REG_SWRESET_RESET BIT(0)
+
+#define ADC5120_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define ADC5120_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define ADC5120_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define ADC5120_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+
+#define PCM1690_REG_MODE_CTRL PCMDEVICE_REG(0X0, 0x46)
+#define PCM1690_REG_MODE_CTRL_DAMS_MSK BIT(7)
+#define PCM1690_REG_MODE_CTRL_DAMS_FINE_STEP 0x0
+#define PCM1690_REG_MODE_CTRL_DAMS_WIDE_RANGE 0x80
+
+#define PCM1690_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM1690_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCM1690_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4a)
+#define PCM1690_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4b)
+#define PCM1690_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM1690_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM1690_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4e)
+#define PCM1690_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4f)
+
+#define PCM6240_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6240_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6240_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6240_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6240_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6240_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6240_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6240_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+
+#define PCM6260_REG_CH1_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x3d)
+#define PCM6260_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3e)
+#define PCM6260_REG_CH2_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x42)
+#define PCM6260_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCM6260_REG_CH3_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x47)
+#define PCM6260_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCM6260_REG_CH4_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x4c)
+#define PCM6260_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4d)
+#define PCM6260_REG_CH5_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x51)
+#define PCM6260_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCM6260_REG_CH6_ANALOG_GAIN PCMDEVICE_REG(0X0, 0x56)
+#define PCM6260_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+
+#define PCM9211_REG_SW_CTRL PCMDEVICE_REG(0X0, 0x40)
+#define PCM9211_REG_SW_CTRL_MRST_MSK BIT(7)
+#define PCM9211_REG_SW_CTRL_MRST 0x0
+
+#define PCM9211_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x46)
+#define PCM9211_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x47)
+
+#define PCMD3140_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3140_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3140_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3140_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+
+#define PCMD3140_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3140_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3140_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3140_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+
+#define PCMD3180_REG_CH1_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x3E)
+#define PCMD3180_REG_CH2_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x43)
+#define PCMD3180_REG_CH3_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x48)
+#define PCMD3180_REG_CH4_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x4D)
+#define PCMD3180_REG_CH5_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x52)
+#define PCMD3180_REG_CH6_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x57)
+#define PCMD3180_REG_CH7_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define PCMD3180_REG_CH8_DIGITAL_GAIN PCMDEVICE_REG(0X0, 0x61)
+
+#define PCMD3180_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x3F)
+#define PCMD3180_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x44)
+#define PCMD3180_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x49)
+#define PCMD3180_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x4E)
+#define PCMD3180_REG_CH5_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define PCMD3180_REG_CH6_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define PCMD3180_REG_CH7_FINE_GAIN PCMDEVICE_REG(0X0, 0x5D)
+#define PCMD3180_REG_CH8_FINE_GAIN PCMDEVICE_REG(0X0, 0x62)
+
+#define TAA5412_REG_CH1_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x52)
+#define TAA5412_REG_CH2_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x57)
+#define TAA5412_REG_CH3_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5B)
+#define TAA5412_REG_CH4_DIGITAL_VOLUME PCMDEVICE_REG(0X0, 0x5F)
+
+#define TAA5412_REG_CH1_FINE_GAIN PCMDEVICE_REG(0X0, 0x53)
+#define TAA5412_REG_CH2_FINE_GAIN PCMDEVICE_REG(0X0, 0x58)
+#define TAA5412_REG_CH3_FINE_GAIN PCMDEVICE_REG(0X0, 0x5C)
+#define TAA5412_REG_CH4_FINE_GAIN PCMDEVICE_REG(0X0, 0x60)
+
+#define PCMDEVICE_CMD_SING_W 0x1
+#define PCMDEVICE_CMD_BURST 0x2
+#define PCMDEVICE_CMD_DELAY 0x3
+#define PCMDEVICE_CMD_FIELD_W 0x4
+
+enum pcmdevice_bin_blk_type {
+ PCMDEVICE_BIN_BLK_COEFF = 1,
+ PCMDEVICE_BIN_BLK_POST_POWER_UP,
+ PCMDEVICE_BIN_BLK_PRE_SHUTDOWN,
+ PCMDEVICE_BIN_BLK_PRE_POWER_UP,
+ PCMDEVICE_BIN_BLK_POST_SHUTDOWN
+};
+
+enum pcmdevice_fw_state {
+ PCMDEVICE_FW_LOAD_OK = 0,
+ PCMDEVICE_FW_LOAD_FAILED
+};
+
+struct pcmdevice_regbin_hdr {
+ unsigned int img_sz;
+ unsigned int checksum;
+ unsigned int binary_version_num;
+ unsigned int drv_fw_version;
+ unsigned int timestamp;
+ unsigned char plat_type;
+ unsigned char dev_family;
+ unsigned char reserve;
+ unsigned char ndev;
+ unsigned char devs[PCMDEVICE_MAX_REGBIN_DEVICES];
+ unsigned int nconfig;
+ unsigned int config_size[PCMDEVICE_CONFIG_SUM];
+};
+
+struct pcmdevice_block_data {
+ unsigned char dev_idx;
+ unsigned char block_type;
+ unsigned short yram_checksum;
+ unsigned int block_size;
+ unsigned int n_subblks;
+ unsigned char *regdata;
+};
+
+struct pcmdevice_config_info {
+ char cfg_name[64];
+ unsigned int nblocks;
+ unsigned int real_nblocks;
+ unsigned char active_dev;
+ struct pcmdevice_block_data **blk_data;
+};
+
+struct pcmdevice_regbin {
+ struct pcmdevice_regbin_hdr fw_hdr;
+ int ncfgs;
+ struct pcmdevice_config_info **cfg_info;
+};
+
+struct pcmdevice_priv {
+ struct snd_soc_component *component;
+ struct i2c_client *client;
+ struct device *dev;
+ struct mutex codec_lock;
+ struct gpio_desc *hw_rst;
+ struct regmap *regmap;
+ struct pcmdevice_regbin regbin;
+ int irq;
+ unsigned int addr[PCMDEVICE_MAX_I2C_DEVICES];
+ unsigned int chip_id;
+ int cur_conf;
+ int fw_state;
+ int ndev;
+ unsigned char bin_name[PCMDEVICE_BIN_FILENAME_LEN];
+ /* used for kcontrol name */
+ unsigned char upper_dev_name[I2C_NAME_SIZE];
+ unsigned char dev_name[I2C_NAME_SIZE];
+};
+
+/* mixer control */
+struct pcmdevice_mixer_control {
+ int max;
+ int reg;
+ unsigned int dev_no;
+ unsigned int shift;
+ unsigned int invert;
+};
+struct pcmdev_ctrl_info {
+ const unsigned int *gain;
+ const struct pcmdevice_mixer_control *pcmdev_ctrl;
+ unsigned int ctrl_array_size;
+ snd_kcontrol_get_t *get;
+ snd_kcontrol_put_t *put;
+ int pcmdev_ctrl_name_id;
+};
+#endif /* __PCM6240_H__ */
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
new file mode 100644
index 000000000000..c0c5b3c3e98b
--- /dev/null
+++ b/sound/soc/codecs/peb2466.c
@@ -0,0 +1,2064 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// peb2466.c -- Infineon PEB2466 ALSA SoC driver
+//
+// Copyright 2023 CS GROUP France
+//
+// Author: Herve Codina <herve.codina@bootlin.com>
+
+#include <linux/unaligned.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PEB2466_NB_CHANNEL 4
+
+struct peb2466_lookup {
+ u8 (*table)[4];
+ unsigned int count;
+};
+
+#define PEB2466_TLV_SIZE ARRAY_SIZE(((unsigned int[]){TLV_DB_SCALE_ITEM(0, 0, 0)}))
+
+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);
+ if (!ret)
+ *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 const 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 int 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 -EINVAL;
+ }
+
+ 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;
+ }
+
+ 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);
+ }
+
+ return 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;
+ }
+
+ ret = peb2466_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
+
+ 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_enabled(&peb2466->spi->dev, "mclk");
+ if (IS_ERR(peb2466->mclk))
+ return PTR_ERR(peb2466->mclk);
+
+ 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:
+ return ret;
+}
+
+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,
+};
+
+module_spi_driver(peb2466_spi_driver);
+
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
+MODULE_DESCRIPTION("PEB2466 ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c
new file mode 100644
index 000000000000..4ed09fbe3f54
--- /dev/null
+++ b/sound/soc/codecs/pm4125-sdw.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright, 2025 Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include "pm4125.h"
+
+static struct pm4125_sdw_ch_info pm4125_sdw_rx_ch_info[] = {
+ WCD_SDW_CH(PM4125_HPH_L, PM4125_HPH_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_HPH_R, PM4125_HPH_PORT, BIT(1)),
+};
+
+static struct pm4125_sdw_ch_info pm4125_sdw_tx_ch_info[] = {
+ WCD_SDW_CH(PM4125_ADC1, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(0)),
+ WCD_SDW_CH(PM4125_ADC2, PM4125_ADC_1_2_DMIC1L_BCS_PORT, BIT(1)),
+};
+
+static struct sdw_dpn_prop pm4125_dpn_prop[PM4125_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,
+ }
+};
+
+struct device *pm4125_sdw_device_get(struct device_node *np)
+{
+ return bus_find_device_by_of_node(&sdw_bus_type, np);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_device_get);
+
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *priv, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ unsigned long ch_mask;
+ int i, j;
+
+ priv->sconfig.ch_count = 1;
+ priv->active_ports = 0;
+ for (i = 0; i < PM4125_MAX_SWR_PORTS; i++) {
+ ch_mask = priv->port_config[i].ch_mask;
+ if (!ch_mask)
+ continue;
+
+ for_each_set_bit(j, &ch_mask, 4)
+ priv->sconfig.ch_count++;
+
+ port_config[priv->active_ports] = priv->port_config[i];
+ priv->active_ports++;
+ }
+
+ priv->sconfig.bps = 1;
+ priv->sconfig.frame_rate = params_rate(params);
+ priv->sconfig.direction = priv->is_tx ? SDW_DATA_DIR_TX : SDW_DATA_DIR_RX;
+ priv->sconfig.type = SDW_STREAM_PCM;
+
+ return sdw_stream_add_slave(priv->sdev, &priv->sconfig, &port_config[0], priv->active_ports,
+ priv->sruntime);
+}
+EXPORT_SYMBOL_GPL(pm4125_sdw_hw_params);
+
+static int pm4125_update_status(struct sdw_slave *slave, enum sdw_slave_status status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+
+ if (priv->regmap && status == SDW_SLAVE_ATTACHED) {
+ /* Write out any cached changes that happened between probe and attach */
+ regcache_cache_only(priv->regmap, false);
+ return regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle Soundwire out-of-band interrupt event by triggering the first irq of the slave_irq
+ * irq domain, which then will be handled by the regmap_irq threaded irq.
+ * Looping is to ensure no interrupts were missed in the process.
+ */
+static int pm4125_interrupt_callback(struct sdw_slave *slave, struct sdw_slave_intr_status *status)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(&slave->dev);
+ struct irq_domain *slave_irq = priv->slave_irq;
+ u32 sts1, sts2, sts3;
+
+ do {
+ handle_nested_irq(irq_find_mapping(slave_irq, 0));
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_0, &sts1);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_1, &sts2);
+ regmap_read(priv->regmap, PM4125_DIG_SWR_INTR_STATUS_2, &sts3);
+
+ } while (sts1 || sts2 || sts3);
+
+ return IRQ_HANDLED;
+}
+
+static const struct reg_default pm4125_defaults[] = {
+ { PM4125_ANA_MICBIAS_MICB_1_2_EN, 0x01 },
+ { PM4125_ANA_MICBIAS_MICB_3_EN, 0x00 },
+ { PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x21 },
+ { PM4125_ANA_MICBIAS_LDO_1_CTRL, 0x01 },
+ { PM4125_ANA_TX_AMIC1, 0x00 },
+ { PM4125_ANA_TX_AMIC2, 0x00 },
+ { PM4125_ANA_MBHC_MECH, 0x39 },
+ { PM4125_ANA_MBHC_ELECT, 0x08 },
+ { PM4125_ANA_MBHC_ZDET, 0x10 },
+ { PM4125_ANA_MBHC_RESULT_1, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_2, 0x00 },
+ { PM4125_ANA_MBHC_RESULT_3, 0x00 },
+ { PM4125_ANA_MBHC_BTN0_ZDET_VREF1, 0x00 },
+ { PM4125_ANA_MBHC_BTN1_ZDET_VREF2, 0x10 },
+ { PM4125_ANA_MBHC_BTN2_ZDET_VREF3, 0x20 },
+ { PM4125_ANA_MBHC_BTN3_ZDET_DBG_400, 0x30 },
+ { PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400, 0x40 },
+ { PM4125_ANA_MBHC_MICB2_RAMP, 0x00 },
+ { PM4125_ANA_MBHC_CTL_1, 0x02 },
+ { PM4125_ANA_MBHC_CTL_2, 0x05 },
+ { PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0xE9 },
+ { PM4125_ANA_MBHC_ZDET_ANA_CTL, 0x0F },
+ { PM4125_ANA_MBHC_ZDET_RAMP_CTL, 0x00 },
+ { PM4125_ANA_MBHC_FSM_STATUS, 0x00 },
+ { PM4125_ANA_MBHC_ADC_RESULT, 0x00 },
+ { PM4125_ANA_MBHC_CTL_CLK, 0x30 },
+ { PM4125_ANA_MBHC_ZDET_CALIB_RESULT, 0x00 },
+ { PM4125_ANA_NCP_EN, 0x00 },
+ { PM4125_ANA_NCP_VCTRL, 0xA7 },
+ { PM4125_ANA_HPHPA_CNP_CTL_1, 0x54 },
+ { PM4125_ANA_HPHPA_CNP_CTL_2, 0x2B },
+ { PM4125_ANA_HPHPA_PA_STATUS, 0x00 },
+ { PM4125_ANA_HPHPA_FSM_CLK, 0x12 },
+ { PM4125_ANA_HPHPA_L_GAIN, 0x00 },
+ { PM4125_ANA_HPHPA_R_GAIN, 0x00 },
+ { PM4125_SWR_HPHPA_HD2, 0x1B },
+ { PM4125_ANA_HPHPA_SPARE_CTL, 0x02 },
+ { PM4125_ANA_SURGE_EN, 0x38 },
+ { PM4125_ANA_COMBOPA_CTL, 0x35 },
+ { PM4125_ANA_COMBOPA_CTL_4, 0x84 },
+ { PM4125_ANA_COMBOPA_CTL_5, 0x05 },
+ { PM4125_ANA_RXLDO_CTL, 0x86 },
+ { PM4125_ANA_MBIAS_EN, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID0, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID1, 0x00 },
+ { PM4125_DIG_SWR_CHIP_ID2, 0x0C },
+ { PM4125_DIG_SWR_CHIP_ID3, 0x01 },
+ { PM4125_DIG_SWR_SWR_TX_CLK_RATE, 0x00 },
+ { PM4125_DIG_SWR_CDC_RST_CTL, 0x03 },
+ { PM4125_DIG_SWR_TOP_CLK_CFG, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_CLK_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX_CLK_CTL, 0x33 },
+ { PM4125_DIG_SWR_SWR_RST_EN, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX0_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_RX1_CTL, 0xFC },
+ { PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1, 0x00 },
+ { PM4125_DIG_SWR_CDC_COMP_CTL_0, 0x00 },
+ { PM4125_DIG_SWR_CDC_RX_DELAY_CTL, 0x66 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_0, 0x55 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_1, 0xA9 },
+ { PM4125_DIG_SWR_CDC_RX_GAIN_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_TX0_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX1_CTL, 0x68 },
+ { PM4125_DIG_SWR_CDC_TX_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_REQ0_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_REQ1_CTL, 0x01 },
+ { PM4125_DIG_SWR_CDC_RST, 0x00 },
+ { PM4125_DIG_SWR_CDC_AMIC_CTL, 0x02 },
+ { PM4125_DIG_SWR_CDC_DMIC_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_DMIC1_RATE, 0x01 },
+ { PM4125_DIG_SWR_PDM_WD_CTL0, 0x00 },
+ { PM4125_DIG_SWR_PDM_WD_CTL1, 0x00 },
+ { PM4125_DIG_SWR_INTR_MODE, 0x00 },
+ { PM4125_DIG_SWR_INTR_MASK_0, 0xFF },
+ { PM4125_DIG_SWR_INTR_MASK_1, 0x7F },
+ { PM4125_DIG_SWR_INTR_MASK_2, 0x0C },
+ { PM4125_DIG_SWR_INTR_STATUS_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_STATUS_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_1, 0x00 },
+ { PM4125_DIG_SWR_INTR_CLEAR_2, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_0, 0x00 },
+ { PM4125_DIG_SWR_INTR_LEVEL_1, 0x2A },
+ { PM4125_DIG_SWR_INTR_LEVEL_2, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX0_CTL, 0x00 },
+ { PM4125_DIG_SWR_CDC_CONN_RX1_CTL, 0x00 },
+ { PM4125_DIG_SWR_LOOP_BACK_MODE, 0x00 },
+ { PM4125_DIG_SWR_DRIVE_STRENGTH_0, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_CTL, 0x00 },
+ { PM4125_DIG_SWR_DIG_DEBUG_EN, 0x00 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA0, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA1, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA2, 0x55 },
+ { PM4125_DIG_SWR_DEM_BYPASS_DATA3, 0x01 },
+};
+
+static bool pm4125_rdwr_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MICBIAS_MICB_1_2_EN:
+ case PM4125_ANA_MICBIAS_MICB_3_EN:
+ case PM4125_ANA_MICBIAS_LDO_1_SETTING:
+ case PM4125_ANA_MICBIAS_LDO_1_CTRL:
+ case PM4125_ANA_TX_AMIC1:
+ case PM4125_ANA_TX_AMIC2:
+ case PM4125_ANA_MBHC_MECH:
+ case PM4125_ANA_MBHC_ELECT:
+ case PM4125_ANA_MBHC_ZDET:
+ case PM4125_ANA_MBHC_BTN0_ZDET_VREF1:
+ case PM4125_ANA_MBHC_BTN1_ZDET_VREF2:
+ case PM4125_ANA_MBHC_BTN2_ZDET_VREF3:
+ case PM4125_ANA_MBHC_BTN3_ZDET_DBG_400:
+ case PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400:
+ case PM4125_ANA_MBHC_MICB2_RAMP:
+ case PM4125_ANA_MBHC_CTL_1:
+ case PM4125_ANA_MBHC_CTL_2:
+ case PM4125_ANA_MBHC_PLUG_DETECT_CTL:
+ case PM4125_ANA_MBHC_ZDET_ANA_CTL:
+ case PM4125_ANA_MBHC_ZDET_RAMP_CTL:
+ case PM4125_ANA_MBHC_CTL_CLK:
+ case PM4125_ANA_NCP_EN:
+ case PM4125_ANA_NCP_VCTRL:
+ case PM4125_ANA_HPHPA_CNP_CTL_1:
+ case PM4125_ANA_HPHPA_CNP_CTL_2:
+ case PM4125_ANA_HPHPA_FSM_CLK:
+ case PM4125_ANA_HPHPA_L_GAIN:
+ case PM4125_ANA_HPHPA_R_GAIN:
+ case PM4125_ANA_HPHPA_SPARE_CTL:
+ case PM4125_SWR_HPHPA_HD2:
+ case PM4125_ANA_SURGE_EN:
+ case PM4125_ANA_COMBOPA_CTL:
+ case PM4125_ANA_COMBOPA_CTL_4:
+ case PM4125_ANA_COMBOPA_CTL_5:
+ case PM4125_ANA_RXLDO_CTL:
+ case PM4125_ANA_MBIAS_EN:
+ case PM4125_DIG_SWR_SWR_TX_CLK_RATE:
+ case PM4125_DIG_SWR_CDC_RST_CTL:
+ case PM4125_DIG_SWR_TOP_CLK_CFG:
+ case PM4125_DIG_SWR_CDC_RX_CLK_CTL:
+ case PM4125_DIG_SWR_CDC_TX_CLK_CTL:
+ case PM4125_DIG_SWR_SWR_RST_EN:
+ case PM4125_DIG_SWR_CDC_RX_RST:
+ case PM4125_DIG_SWR_CDC_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_RX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1:
+ case PM4125_DIG_SWR_CDC_COMP_CTL_0:
+ case PM4125_DIG_SWR_CDC_RX_DELAY_CTL:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_0:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_1:
+ case PM4125_DIG_SWR_CDC_RX_GAIN_CTL:
+ case PM4125_DIG_SWR_CDC_TX0_CTL:
+ case PM4125_DIG_SWR_CDC_TX1_CTL:
+ case PM4125_DIG_SWR_CDC_TX_RST:
+ case PM4125_DIG_SWR_CDC_REQ0_CTL:
+ case PM4125_DIG_SWR_CDC_REQ1_CTL:
+ case PM4125_DIG_SWR_CDC_RST:
+ case PM4125_DIG_SWR_CDC_AMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_CTL:
+ case PM4125_DIG_SWR_CDC_DMIC1_RATE:
+ case PM4125_DIG_SWR_PDM_WD_CTL0:
+ case PM4125_DIG_SWR_PDM_WD_CTL1:
+ case PM4125_DIG_SWR_INTR_MODE:
+ case PM4125_DIG_SWR_INTR_MASK_0:
+ case PM4125_DIG_SWR_INTR_MASK_1:
+ case PM4125_DIG_SWR_INTR_MASK_2:
+ case PM4125_DIG_SWR_INTR_CLEAR_0:
+ case PM4125_DIG_SWR_INTR_CLEAR_1:
+ case PM4125_DIG_SWR_INTR_CLEAR_2:
+ case PM4125_DIG_SWR_INTR_LEVEL_0:
+ case PM4125_DIG_SWR_INTR_LEVEL_1:
+ case PM4125_DIG_SWR_INTR_LEVEL_2:
+ case PM4125_DIG_SWR_CDC_CONN_RX0_CTL:
+ case PM4125_DIG_SWR_CDC_CONN_RX1_CTL:
+ case PM4125_DIG_SWR_LOOP_BACK_MODE:
+ case PM4125_DIG_SWR_DRIVE_STRENGTH_0:
+ case PM4125_DIG_SWR_DIG_DEBUG_CTL:
+ case PM4125_DIG_SWR_DIG_DEBUG_EN:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA0:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA1:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA2:
+ case PM4125_DIG_SWR_DEM_BYPASS_DATA3:
+ return true;
+ }
+
+ return false;
+}
+
+static bool pm4125_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+ return pm4125_rdwr_register(dev, reg);
+}
+
+static bool pm4125_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case PM4125_ANA_MBHC_RESULT_1:
+ case PM4125_ANA_MBHC_RESULT_2:
+ case PM4125_ANA_MBHC_RESULT_3:
+ case PM4125_ANA_MBHC_FSM_STATUS:
+ case PM4125_ANA_MBHC_ADC_RESULT:
+ case PM4125_ANA_MBHC_ZDET_CALIB_RESULT:
+ case PM4125_ANA_HPHPA_PA_STATUS:
+ case PM4125_DIG_SWR_CHIP_ID0:
+ case PM4125_DIG_SWR_CHIP_ID1:
+ case PM4125_DIG_SWR_CHIP_ID2:
+ case PM4125_DIG_SWR_CHIP_ID3:
+ case PM4125_DIG_SWR_INTR_STATUS_0:
+ case PM4125_DIG_SWR_INTR_STATUS_1:
+ case PM4125_DIG_SWR_INTR_STATUS_2:
+ return true;
+ }
+
+ return false;
+}
+
+static const struct regmap_config pm4125_regmap_config = {
+ .name = "pm4125_csr",
+ .reg_bits = 32,
+ .val_bits = 8,
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = pm4125_defaults,
+ .num_reg_defaults = ARRAY_SIZE(pm4125_defaults),
+ .max_register = PM4125_MAX_REGISTER,
+ .readable_reg = pm4125_readable_register,
+ .writeable_reg = pm4125_rdwr_register,
+ .volatile_reg = pm4125_volatile_register,
+};
+
+static const struct sdw_slave_ops pm4125_slave_ops = {
+ .update_status = pm4125_update_status,
+ .interrupt_callback = pm4125_interrupt_callback,
+};
+
+static int pm4125_sdw_component_bind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static void pm4125_sdw_component_unbind(struct device *dev, struct device *master, void *data)
+{
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct component_ops pm4125_sdw_component_ops = {
+ .bind = pm4125_sdw_component_bind,
+ .unbind = pm4125_sdw_component_unbind,
+};
+
+static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
+{
+ struct device *dev = &pdev->dev;
+ struct pm4125_sdw_priv *priv;
+ u8 master_ch_mask[PM4125_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* Port map index starts at 0, however the data port for this codec starts at index 1 */
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
+ priv->is_tx = true;
+ ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_TX_SWR_PORTS);
+ } else {
+ ret = of_property_read_u32_array(dev->of_node, "qcom,rx-port-mapping",
+ &pdev->m_port_map[1], PM4125_MAX_SWR_PORTS);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Error getting static port mapping for %s (%d)\n",
+ priv->is_tx ? "TX" : "RX", ret);
+
+ priv->sdev = pdev;
+ dev_set_drvdata(dev, priv);
+
+ 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;
+
+ memset(master_ch_mask, 0, PM4125_MAX_SWR_CH_IDS);
+
+ if (priv->is_tx) {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (priv->is_tx) {
+ pdev->prop.source_ports = GENMASK(PM4125_MAX_TX_SWR_PORTS, 0);
+ pdev->prop.src_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+
+ pdev->prop.wake_capable = true;
+
+ priv->regmap = devm_regmap_init_sdw(pdev, &pm4125_regmap_config);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap), "regmap init failed\n");
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(priv->regmap, true);
+ } else {
+ pdev->prop.sink_ports = GENMASK(PM4125_MAX_SWR_PORTS - 1, 0);
+ pdev->prop.sink_dpn_prop = pm4125_dpn_prop;
+ priv->ch_info = &pm4125_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ priv->ch_info[i].master_ch_mask = PM4125_SWRM_CH_MASK(master_ch_mask[i]);
+ }
+
+ ret = component_add(dev, &pm4125_sdw_component_ops);
+ if (ret)
+ return ret;
+
+ /* Set suspended until aggregate device is bind */
+ pm_runtime_set_suspended(dev);
+
+ return 0;
+}
+
+static int pm4125_remove(struct sdw_slave *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_del(dev, &pm4125_sdw_component_ops);
+
+ return 0;
+}
+
+static const struct sdw_device_id pm4125_slave_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x10c, 0), /* Soundwire pm4125 RX/TX Device ID */
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, pm4125_slave_id);
+
+static int __maybe_unused pm4125_sdw_runtime_suspend(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, true);
+ regcache_mark_dirty(priv->regmap);
+ }
+
+ return 0;
+}
+
+static int __maybe_unused pm4125_sdw_runtime_resume(struct device *dev)
+{
+ struct pm4125_sdw_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->regmap) {
+ regcache_cache_only(priv->regmap, false);
+ regcache_sync(priv->regmap);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops pm4125_sdw_pm_ops = {
+ SET_RUNTIME_PM_OPS(pm4125_sdw_runtime_suspend, pm4125_sdw_runtime_resume, NULL)
+};
+
+static struct sdw_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .ops = &pm4125_slave_ops,
+ .id_table = pm4125_slave_id,
+ .driver = {
+ .name = "pm4125-codec",
+ .pm = &pm4125_sdw_pm_ops,
+ }
+};
+module_sdw_driver(pm4125_codec_driver);
+
+MODULE_DESCRIPTION("PM4125 SDW codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
new file mode 100644
index 000000000000..706fc668ffe2
--- /dev/null
+++ b/sound/soc/codecs/pm4125.c
@@ -0,0 +1,1780 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+// Copyright (c) 2025, Linaro Ltd
+
+#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "pm4125.h"
+#include "wcd-mbhc-v2.h"
+
+#define WCD_MBHC_HS_V_MAX 1600
+#define PM4125_MBHC_MAX_BUTTONS 8
+
+#define PM4125_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 PM4125_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800)
+
+#define PM4125_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+/* Registers in SPMI addr space */
+#define PM4125_CODEC_RESET_REG 0xF3DB
+#define PM4125_CODEC_OFF 0x1
+#define PM4125_CODEC_ON 0x0
+#define PM4125_CODEC_FOUNDRY_ID_REG 0x7
+
+enum {
+ HPH_COMP_DELAY,
+ HPH_PA_DELAY,
+ AMIC2_BCS_ENABLE,
+};
+
+enum {
+ AIF1_PB = 0,
+ AIF1_CAP,
+ NUM_CODEC_DAIS,
+};
+
+struct pm4125_priv {
+ struct sdw_slave *tx_sdw_dev;
+ struct pm4125_sdw_priv *sdw_priv[NUM_CODEC_DAIS];
+ struct device *txdev;
+ struct device *rxdev;
+ struct device_node *rxnode;
+ struct device_node *txnode;
+ struct regmap *regmap;
+ struct regmap *spmi_regmap;
+ /* mbhc module */
+ struct wcd_mbhc *wcd_mbhc;
+ struct wcd_mbhc_config mbhc_cfg;
+ struct wcd_mbhc_intr intr_ids;
+ struct irq_domain *virq;
+ const struct regmap_irq_chip *pm4125_regmap_irq_chip;
+ struct regmap_irq_chip_data *irq_chip;
+ struct snd_soc_jack *jack;
+ unsigned long status_mask;
+ s32 micb_ref[PM4125_MAX_MICBIAS];
+ s32 pullup_ref[PM4125_MAX_MICBIAS];
+ u32 micb1_mv;
+ u32 micb2_mv;
+ u32 micb3_mv;
+
+ int hphr_pdm_wd_int;
+ int hphl_pdm_wd_int;
+ bool comp1_enable;
+ bool comp2_enable;
+
+ atomic_t gloal_mbias_cnt;
+};
+
+static const char * const pm4125_power_supplies[] = {
+ "vdd-io", "vdd-cp", "vdd-mic-bias", "vdd-pa-vpos",
+};
+
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static const struct wcd_mbhc_field pm4125_mbhc_fields[WCD_MBHC_REG_FUNC_MAX] = {
+ WCD_MBHC_FIELD(WCD_MBHC_L_DET_EN, PM4125_ANA_MBHC_MECH, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_DET_EN, PM4125_ANA_MBHC_MECH, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_MECH_DETECTION_TYPE, PM4125_ANA_MBHC_MECH, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_CLAMP_CTL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x30),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_DETECTION_TYPE, PM4125_ANA_MBHC_ELECT, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x1F),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, PM4125_ANA_MBHC_MECH, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_GND_PLUG_TYPE, PM4125_ANA_MBHC_MECH, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_SW_HPH_LP_100K_TO_GND, PM4125_ANA_MBHC_MECH, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_SCHMT_ISRC, PM4125_ANA_MBHC_ELECT, 0x06),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_EN, PM4125_ANA_MBHC_ELECT, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_INSREM_DBNC, PM4125_ANA_MBHC_PLUG_DETECT_CTL, 0x0F),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_DBNC, PM4125_ANA_MBHC_CTL_1, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_VREF, PM4125_ANA_MBHC_CTL_2, 0x03),
+ WCD_MBHC_FIELD(WCD_MBHC_HS_COMP_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_IN2P_CLAMP_STATE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_MIC_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_SCHMT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_RESULT, PM4125_ANA_MBHC_RESULT_3, 0x07),
+ WCD_MBHC_FIELD(WCD_MBHC_BTN_ISRC_CTL, PM4125_ANA_MBHC_ELECT, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_RESULT, PM4125_ANA_MBHC_RESULT_3, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB_CTRL, PM4125_ANA_MICBIAS_MICB_1_2_EN, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPH_PA_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0xC0),
+ WCD_MBHC_FIELD(WCD_MBHC_SWCH_LEVEL_REMOVE, PM4125_ANA_MBHC_RESULT_3, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_FSM_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_MUX_CTL, PM4125_ANA_MBHC_CTL_2, 0x70),
+ WCD_MBHC_FIELD(WCD_MBHC_MOISTURE_STATUS, PM4125_ANA_MBHC_FSM_STATUS, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_DET_EN, PM4125_ANA_HPHPA_CNP_CTL_2, 0x01),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHL_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_HPHR_OCP_STATUS, PM4125_DIG_SWR_INTR_STATUS_0, 0x20),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_EN, PM4125_ANA_MBHC_CTL_1, 0x08),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_COMPLETE, PM4125_ANA_MBHC_FSM_STATUS, 0x40),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_TIMEOUT, PM4125_ANA_MBHC_FSM_STATUS, 0x80),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_RESULT, PM4125_ANA_MBHC_ADC_RESULT, 0xFF),
+ WCD_MBHC_FIELD(WCD_MBHC_MICB2_VOUT, PM4125_ANA_MICBIAS_LDO_1_SETTING, 0x3F),
+ WCD_MBHC_FIELD(WCD_MBHC_ADC_MODE, PM4125_ANA_MBHC_CTL_1, 0x10),
+ WCD_MBHC_FIELD(WCD_MBHC_DETECTION_DONE, PM4125_ANA_MBHC_CTL_1, 0x04),
+ WCD_MBHC_FIELD(WCD_MBHC_ELECT_ISRC_EN, PM4125_ANA_MBHC_ZDET, 0x02),
+};
+
+static const struct regmap_irq pm4125_irqs[PM4125_NUM_IRQS] = {
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_PRESS_DET, 0, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_BUTTON_RELEASE_DET, 0, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_DET, 0, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_SW_DET, 0, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_OCP_INT, 0, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_CNP_INT, 0, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_OCP_INT, 0, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_CNP_INT, 1, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_CNP_INT, 1, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_EAR_SCD_INT, 1, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_CNP_INT, 1, BIT(3)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_SCD_INT, 1, BIT(4)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_PDM_WD_INT, 1, BIT(5)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_PDM_WD_INT, 1, BIT(6)),
+ REGMAP_IRQ_REG(PM4125_IRQ_AUX_PDM_WD_INT, 1, BIT(7)),
+ REGMAP_IRQ_REG(PM4125_IRQ_LDORT_SCD_INT, 2, BIT(0)),
+ REGMAP_IRQ_REG(PM4125_IRQ_MBHC_MOISTURE_INT, 2, BIT(1)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHL_SURGE_DET_INT, 2, BIT(2)),
+ REGMAP_IRQ_REG(PM4125_IRQ_HPHR_SURGE_DET_INT, 2, BIT(3)),
+};
+
+static int pm4125_handle_post_irq(void *data)
+{
+ struct pm4125_priv *pm4125 = (struct pm4125_priv *)data;
+
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_0, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_1, 0);
+ regmap_write(pm4125->regmap, PM4125_DIG_SWR_INTR_CLEAR_2, 0);
+
+ return IRQ_HANDLED;
+}
+
+static const u32 pm4125_config_regs[] = {
+ PM4125_DIG_SWR_INTR_LEVEL_0,
+};
+
+static struct regmap_irq_chip pm4125_regmap_irq_chip = {
+ .name = "pm4125",
+ .irqs = pm4125_irqs,
+ .num_irqs = ARRAY_SIZE(pm4125_irqs),
+ .num_regs = 3,
+ .status_base = PM4125_DIG_SWR_INTR_STATUS_0,
+ .mask_base = PM4125_DIG_SWR_INTR_MASK_0,
+ .ack_base = PM4125_DIG_SWR_INTR_CLEAR_0,
+ .use_ack = 1,
+ .clear_ack = 1,
+ .config_base = pm4125_config_regs,
+ .num_config_bases = ARRAY_SIZE(pm4125_config_regs),
+ .num_config_regs = 1,
+ .runtime_pm = true,
+ .handle_post_irq = pm4125_handle_post_irq,
+};
+
+static void pm4125_reset(struct pm4125_priv *pm4125)
+{
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_OFF);
+ usleep_range(20, 30);
+ regmap_write(pm4125->spmi_regmap, PM4125_CODEC_RESET_REG, PM4125_CODEC_ON);
+ usleep_range(5000, 5010);
+}
+
+static void pm4125_io_init(struct regmap *regmap)
+{
+ /* Disable HPH OCP */
+ regmap_update_bits(regmap, PM4125_ANA_HPHPA_CNP_CTL_2,
+ PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK | PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK,
+ PM4125_ANA_HPHPA_CNP_OCP_DISABLE);
+
+ /* Enable surge protection */
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHL_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+ regmap_update_bits(regmap, PM4125_ANA_SURGE_EN, PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ FIELD_PREP(PM4125_ANA_SURGE_PROTECTION_HPHR_MASK,
+ PM4125_ANA_SURGE_PROTECTION_ENABLE));
+
+ /* Disable mic bias 2 pull down */
+ regmap_update_bits(regmap, PM4125_ANA_MICBIAS_MICB_1_2_EN,
+ PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ FIELD_PREP(PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK,
+ PM4125_ANA_MICBIAS_MICB_PULL_DISABLE));
+}
+
+static int pm4125_global_mbias_disable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_dec_and_test(&pm4125->gloal_mbias_cnt)) {
+
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_DISABLE);
+ }
+
+ return 0;
+}
+
+static int pm4125_global_mbias_enable(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ if (atomic_inc_return(&pm4125->gloal_mbias_cnt) == 1) {
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_GLOBAL_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_MBIAS_EN,
+ PM4125_ANA_MBIAS_EN_V2I_MASK,
+ PM4125_ANA_MBIAS_EN_ENABLE);
+ usleep_range(1000, 1100);
+ }
+
+ return 0;
+}
+
+static int pm4125_rx_clk_enable(struct snd_soc_component *component)
+{
+ pm4125_global_mbias_enable(component);
+
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ usleep_range(5000, 5100);
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_68);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE);
+ snd_soc_component_update_bits(component, PM4125_ANA_NCP_VCTRL, 0x07, 0x06);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_ENABLE);
+ usleep_range(500, 510);
+
+ return 0;
+}
+
+static int pm4125_rx_clk_disable(struct snd_soc_component *component)
+{
+
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK,
+ PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE);
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_FSM_CLK,
+ PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK,
+ 0x00);
+ snd_soc_component_write_field(component, PM4125_ANA_NCP_EN,
+ PM4125_ANA_NCP_ENABLE_MASK,
+ PM4125_ANA_NCP_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+
+ pm4125_global_mbias_disable(component);
+
+ return 0;
+}
+
+
+static int pm4125_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:
+ pm4125_rx_clk_enable(component);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_rx_clk_disable(component);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_LEFT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp1_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled as per
+ * HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_ANA_HPHPA_CNP_CTL_1,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK,
+ PM4125_ANA_HPHPA_CNP_CTL_1_EN);
+ snd_soc_component_write_field(component, PM4125_SWR_HPHPA_HD2,
+ PM4125_SWR_HPHPA_HD2_RIGHT_MASK,
+ PM4125_SWR_HPHPA_HD2_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (pm4125->comp2_enable) {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ if (pm4125->comp1_enable)
+ snd_soc_component_write_field(component,
+ PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHL_EN_MASK,
+ PM4125_DIG_SWR_COMP_ENABLE);
+ /*
+ * 5ms sleep is required after COMP is enabled
+ * as per HW requirement
+ */
+ usleep_range(5000, 5100);
+ } else {
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ }
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX1_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX1_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX1_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ if (pm4125->comp2_enable)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_COMP_CTL_0,
+ PM4125_DIG_SWR_COMP_HPHR_EN_MASK,
+ PM4125_DIG_SWR_COMP_DISABLE);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_ear_lo_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_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_ENABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_CLK_CTL,
+ PM4125_DIG_SWR_RX0_CLK_EN_MASK,
+ PM4125_DIG_SWR_RX_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX_GAIN_CTL,
+ PM4125_DIG_SWR_RX0_EN_MASK,
+ PM4125_DIG_SWR_RX_INPUT_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_RX0_CTL,
+ PM4125_DIG_SWR_DSM_DITHER_EN_MASK,
+ PM4125_DIG_SWR_DSM_DITHER_ENABLE);
+ break;
+ }
+
+ return 0;
+}
+
+
+static int pm4125_codec_enable_hphl_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphl_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_hphr_wdt_irq(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ enable_irq(pm4125->hphr_pdm_wd_int);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX1_M | PM4125_WDT_ENABLE_RX1_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL1,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ usleep_range(200, 210);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_codec_enable_lo_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, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_LO);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(2000, 2010);
+ snd_soc_component_write_field(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ usleep_range(5000, 5100);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_5, 0x04, 0x00);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x0F);
+ usleep_range(1000, 1010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL,
+ PM4125_ANA_COMBO_PA_SELECT_MASK,
+ PM4125_ANA_COMBO_PA_SELECT_EAR);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK,
+ (PM4125_WDT_ENABLE_RX0_M | PM4125_WDT_ENABLE_RX0_L));
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5010);
+ snd_soc_component_update_bits(component, PM4125_ANA_COMBOPA_CTL_4, 0x0F, 0x04);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5010);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_PDM_WD_CTL0,
+ PM4125_WDT_ENABLE_MASK, 0x00);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_micb_vout_ctl_val(struct device *dev, u32 micb_mv)
+{
+ if (micb_mv < 1600 || micb_mv > 2850) {
+ dev_err(dev, "%s: unsupported micbias voltage (%u mV)\n", __func__, micb_mv);
+ return -EINVAL;
+ }
+
+ return (micb_mv - 1600) / 50;
+}
+
+static int pm4125_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 pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Enable BCS for Headset mic */
+ if (w->shift == 1 &&
+ !(snd_soc_component_read(component, PM4125_ANA_TX_AMIC2) & 0x10)) {
+ set_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+ }
+ pm4125_global_mbias_enable(component);
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ PM4125_DIG_SWR_TXD_MODE_NORMAL);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (w->shift == 1 && test_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask))
+ clear_bit(AMIC2_BCS_ENABLE, &pm4125->status_mask);
+
+ if (w->shift)
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK,
+ 0x00);
+ else
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+ PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK,
+ 0x00);
+ pm4125_global_mbias_disable(component);
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_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 = w->reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_DMIC1);
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_ENABLE);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_component_update_bits(component, dmic_clk_reg,
+ PM4125_DIG_SWR_DMIC1_CLK_EN_MASK,
+ PM4125_DIG_SWR_DMIC1_CLK_DISABLE);
+ snd_soc_component_write_field(component, PM4125_DIG_SWR_CDC_AMIC_CTL,
+ PM4125_DIG_SWR_AMIC_SELECT_MASK,
+ PM4125_DIG_SWR_AMIC_SELECT_AMIC3);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_micbias_control(struct snd_soc_component *component, int micb_num, int req,
+ bool is_dapm)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ int micb_index = micb_num - 1;
+ u16 micb_reg;
+ u8 pullup_mask = 0, enable_mask = 0;
+
+ if ((micb_index < 0) || (micb_index > PM4125_MAX_MICBIAS - 1)) {
+ dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+ __func__, micb_index);
+ return -EINVAL;
+ }
+ switch (micb_num) {
+ case MIC_BIAS_1:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK;
+ enable_mask = 0x40;
+ break;
+ case MIC_BIAS_2:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_1_2_EN;
+ pullup_mask = PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK;
+ enable_mask = 0x04;
+ break;
+ case MIC_BIAS_3:
+ micb_reg = PM4125_ANA_MICBIAS_MICB_3_EN;
+ pullup_mask = 0x02;
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+ __func__, micb_num);
+ return -EINVAL;
+ };
+
+ switch (req) {
+ case MICB_PULLUP_ENABLE:
+ pm4125->pullup_ref[micb_index]++;
+ if ((pm4125->pullup_ref[micb_index] == 1) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ break;
+ case MICB_PULLUP_DISABLE:
+ if (pm4125->pullup_ref[micb_index] > 0)
+ pm4125->pullup_ref[micb_index]--;
+ if ((pm4125->pullup_ref[micb_index] == 0) &&
+ (pm4125->micb_ref[micb_index] == 0))
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, 0x00);
+ break;
+ case MICB_ENABLE:
+ pm4125->micb_ref[micb_index]++;
+ if (pm4125->micb_ref[micb_index] == 1) {
+ pm4125_global_mbias_enable(component);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, enable_mask);
+ }
+ break;
+ case MICB_DISABLE:
+ if (pm4125->micb_ref[micb_index] > 0)
+ pm4125->micb_ref[micb_index]--;
+ if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] > 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ pullup_mask, pullup_mask);
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ } else if ((pm4125->micb_ref[micb_index] == 0) &&
+ (pm4125->pullup_ref[micb_index] == 0)) {
+ snd_soc_component_update_bits(component, micb_reg,
+ enable_mask, 0x00);
+ pm4125_global_mbias_disable(component);
+ }
+ break;
+ };
+
+ return 0;
+}
+
+static int pm4125_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:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (micb_num == MIC_BIAS_3)
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ else
+ pm4125_micbias_control(component, micb_num, MICB_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_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:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_ENABLE, true);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(1000, 1100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ pm4125_micbias_control(component, micb_num, MICB_PULLUP_DISABLE, true);
+ break;
+ }
+
+ return 0;
+}
+
+static int pm4125_connect_port(struct pm4125_sdw_priv *sdw_priv, u8 port_idx, u8 ch_id, bool enable)
+{
+ struct sdw_port_config *port_config = &sdw_priv->port_config[port_idx - 1];
+ const struct pm4125_sdw_ch_info *ch_info = &sdw_priv->ch_info[ch_id];
+ struct sdw_slave *sdev = sdw_priv->sdev;
+ u8 port_num = ch_info->port_num;
+ u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+
+ port_config->num = port_num;
+
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
+ port_config->ch_mask |= ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
+ port_config->ch_mask &= ~ch_mask;
+ sdw_priv->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
+
+ return 0;
+}
+
+static int pm4125_get_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ ucontrol->value.integer.value[0] = hphr ? pm4125->comp2_enable : pm4125->comp1_enable;
+ return 0;
+}
+
+static int pm4125_set_compander(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[AIF1_PB];
+ int value = ucontrol->value.integer.value[0];
+ struct soc_mixer_control *mc;
+ int portidx;
+ bool hphr;
+
+ mc = (struct soc_mixer_control *)(kcontrol->private_value);
+ hphr = mc->shift;
+
+ if (hphr) {
+ if (value == pm4125->comp2_enable)
+ return 0;
+
+ pm4125->comp2_enable = value;
+ } else {
+ if (value == pm4125->comp1_enable)
+ return 0;
+
+ pm4125->comp1_enable = value;
+ }
+
+ portidx = sdw_priv->ch_info[mc->reg].port_num;
+
+ pm4125_connect_port(sdw_priv, portidx, mc->reg, value ? true : false);
+
+ return 1;
+}
+
+static int pm4125_get_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ ucontrol->value.integer.value[0] = sdw_priv->port_enable[portidx];
+
+ return 0;
+}
+
+static int pm4125_set_swr_port(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(comp);
+ struct pm4125_sdw_priv *sdw_priv;
+ int dai_id = mixer->shift;
+ int ch_idx = mixer->reg;
+ int portidx;
+ bool enable;
+
+ sdw_priv = pm4125->sdw_priv[dai_id];
+
+ portidx = sdw_priv->ch_info[ch_idx].port_num;
+
+ enable = ucontrol->value.integer.value[0];
+
+ if (enable == sdw_priv->port_enable[portidx]) {
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+ return 0;
+ }
+
+ sdw_priv->port_enable[portidx] = enable;
+ pm4125_connect_port(sdw_priv, portidx, ch_idx, enable);
+
+ return 1;
+}
+
+static void pm4125_mbhc_bias_control(struct snd_soc_component *component, bool enable)
+{
+ snd_soc_component_write_field(component, PM4125_ANA_MBHC_ELECT,
+ PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK,
+ enable ? PM4125_ANA_MBHC_ELECT_BIAS_ENABLE :
+ PM4125_ANA_MBHC_ELECT_BIAS_DISABLE);
+}
+
+static void pm4125_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, PM4125_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+ PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK, vth << 2);
+ }
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+ .mbhc_bias = pm4125_mbhc_bias_control,
+ .set_btn_thr = pm4125_mbhc_program_btn_thr,
+};
+
+static int pm4125_mbhc_init(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct wcd_mbhc_intr *intr_ids = &pm4125->intr_ids;
+
+ intr_ids->mbhc_sw_intr = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_MBHC_SW_DET);
+
+ intr_ids->mbhc_btn_press_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET);
+
+ intr_ids->mbhc_btn_release_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET);
+
+ intr_ids->mbhc_hs_ins_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET);
+
+ intr_ids->mbhc_hs_rem_intr = regmap_irq_get_virq(pm4125->irq_chip,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET);
+
+ intr_ids->hph_left_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_OCP_INT);
+
+ intr_ids->hph_right_ocp = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_OCP_INT);
+
+ pm4125->wcd_mbhc = wcd_mbhc_init(component, &mbhc_cb, intr_ids, pm4125_mbhc_fields, false);
+ if (IS_ERR(pm4125->wcd_mbhc))
+ return PTR_ERR(pm4125->wcd_mbhc);
+
+ return 0;
+}
+
+static void pm4125_mbhc_deinit(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ wcd_mbhc_deinit(pm4125->wcd_mbhc);
+}
+
+static const struct snd_kcontrol_new pm4125_snd_controls[] = {
+ SOC_SINGLE_EXT("HPHL_COMP Switch", PM4125_COMP_L, 0, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+ SOC_SINGLE_EXT("HPHR_COMP Switch", PM4125_COMP_R, 1, 1, 0,
+ pm4125_get_compander, pm4125_set_compander),
+
+ SOC_SINGLE_TLV("HPHL Volume", PM4125_ANA_HPHPA_L_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("HPHR Volume", PM4125_ANA_HPHPA_R_GAIN, 0, 20, 1,
+ line_gain),
+ SOC_SINGLE_TLV("ADC1 Volume", PM4125_ANA_TX_AMIC1, 0, 8, 0,
+ analog_gain),
+ SOC_SINGLE_TLV("ADC2 Volume", PM4125_ANA_TX_AMIC2, 0, 8, 0,
+ analog_gain),
+
+ SOC_SINGLE_EXT("HPHL Switch", PM4125_HPH_L, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("HPHR Switch", PM4125_HPH_R, 0, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+
+ SOC_SINGLE_EXT("ADC1 Switch", PM4125_ADC1, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+ SOC_SINGLE_EXT("ADC2 Switch", PM4125_ADC2, 1, 1, 0,
+ pm4125_get_swr_port, pm4125_set_swr_port),
+};
+
+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 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 ear_rdac_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_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 char * const adc2_mux_text[] = {
+ "INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum = SOC_ENUM_SINGLE(PM4125_ANA_TX_AMIC2, 4,
+ ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct snd_kcontrol_new tx_adc2_mux = SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static const struct snd_soc_dapm_widget pm4125_dapm_widgets[] = {
+ /* Input widgets */
+ SND_SOC_DAPM_INPUT("AMIC1"),
+ SND_SOC_DAPM_INPUT("AMIC2"),
+ SND_SOC_DAPM_INPUT("AMIC3"),
+ SND_SOC_DAPM_INPUT("IN1_HPHL"),
+ SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_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, pm4125_codec_enable_adc,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux),
+
+ /* TX mixers */
+ SND_SOC_DAPM_MIXER("ADC1_MIXER", SND_SOC_NOPM, 0, 0, adc1_switch, ARRAY_SIZE(adc1_switch)),
+ SND_SOC_DAPM_MIXER("ADC2_MIXER", SND_SOC_NOPM, 1, 0, adc2_switch, ARRAY_SIZE(adc2_switch)),
+
+ /* MIC_BIAS widgets */
+ SND_SOC_DAPM_SUPPLY("MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0, pm4125_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, pm4125_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, pm4125_codec_enable_micbias,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* RX widgets */
+ SND_SOC_DAPM_PGA_E("EAR PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_ear_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("LO PGA", PM4125_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+ pm4125_codec_enable_lo_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHL PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL, 0,
+ pm4125_codec_enable_hphl_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("HPHR PGA", PM4125_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL, 0,
+ pm4125_codec_enable_hphr_pa,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0, pm4125_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, pm4125_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, pm4125_codec_ear_lo_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_SUPPLY("HPHL_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphl_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("HPHR_WDT_IRQ", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_hphr_wdt_irq,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("RXCLK", SND_SOC_NOPM, 0, 0, pm4125_codec_enable_rxclk,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ 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),
+
+ /* 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("LO_RDAC", SND_SOC_NOPM, 0, 0, lo_rdac_switch,
+ ARRAY_SIZE(lo_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)),
+
+ /* TX output widgets */
+ SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+ /* RX output widgets */
+ SND_SOC_DAPM_OUTPUT("EAR"),
+ SND_SOC_DAPM_OUTPUT("LO"),
+ SND_SOC_DAPM_OUTPUT("HPHL"),
+ SND_SOC_DAPM_OUTPUT("HPHR"),
+
+ /* MIC_BIAS pull up widgets */
+ SND_SOC_DAPM_SUPPLY("VA MIC BIAS1", SND_SOC_NOPM, MIC_BIAS_1, 0,
+ pm4125_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,
+ pm4125_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,
+ pm4125_codec_enable_micbias_pullup,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX widgets */
+ SND_SOC_DAPM_ADC_E("DMIC1", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 0, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("DMIC2", NULL, PM4125_DIG_SWR_CDC_DMIC1_CTL, 1, 0,
+ pm4125_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* TX mixer widgets */
+ SND_SOC_DAPM_MIXER("DMIC1_MIXER", SND_SOC_NOPM, 0, 0, dmic1_switch,
+ ARRAY_SIZE(dmic1_switch)),
+ SND_SOC_DAPM_MIXER("DMIC2_MIXER", SND_SOC_NOPM, 1, 0, dmic2_switch,
+ ARRAY_SIZE(dmic2_switch)),
+
+ /* Output widgets */
+ SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+ SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route pm4125_audio_map[] = {
+ { "ADC1_OUTPUT", NULL, "ADC1_MIXER" },
+ { "ADC1_MIXER", "Switch", "ADC1" },
+ { "ADC1", NULL, "AMIC1" },
+
+ { "ADC2_OUTPUT", NULL, "ADC2_MIXER" },
+ { "ADC2_MIXER", "Switch", "ADC2" },
+ { "ADC2", NULL, "ADC2 MUX" },
+ { "ADC2 MUX", "INP3", "AMIC3" },
+ { "ADC2 MUX", "INP2", "AMIC2" },
+
+ { "IN1_HPHL", NULL, "PA_VPOS" },
+ { "RX1", NULL, "IN1_HPHL" },
+ { "RX1", NULL, "RXCLK" },
+ { "RX1", NULL, "HPHL_WDT_IRQ" },
+ { "RDAC1", NULL, "RX1" },
+ { "HPHL_RDAC", "Switch", "RDAC1" },
+ { "HPHL PGA", NULL, "HPHL_RDAC" },
+ { "HPHL", NULL, "HPHL PGA" },
+
+ { "IN2_HPHR", NULL, "PA_VPOS" },
+ { "RX2", NULL, "IN2_HPHR" },
+ { "RX2", NULL, "RXCLK" },
+ { "RX2", NULL, "HPHR_WDT_IRQ" },
+ { "RDAC2", NULL, "RX2" },
+ { "HPHR_RDAC", "Switch", "RDAC2" },
+ { "HPHR PGA", NULL, "HPHR_RDAC" },
+ { "HPHR", NULL, "HPHR PGA" },
+
+ { "RDAC3", NULL, "RX1" },
+ { "EAR_RDAC", "Switch", "RDAC3" },
+ { "EAR PGA", NULL, "EAR_RDAC" },
+ { "EAR", NULL, "EAR PGA" },
+
+ { "LO_RDAC", "Switch", "RDAC3" },
+ { "LO PGA", NULL, "LO_RDAC" },
+ { "LO", NULL, "LO PGA" },
+
+ { "DMIC1_OUTPUT", NULL, "DMIC1_MIXER" },
+ { "DMIC1_MIXER", "Switch", "DMIC1" },
+
+ { "DMIC2_OUTPUT", NULL, "DMIC2_MIXER" },
+ { "DMIC2_MIXER", "Switch", "DMIC2" },
+};
+
+static int pm4125_set_micbias_data(struct device *dev, struct pm4125_priv *pm4125)
+{
+ int vout_ctl;
+
+ /* Set micbias voltage */
+ vout_ctl = pm4125_get_micb_vout_ctl_val(dev, pm4125->micb1_mv);
+ if (vout_ctl < 0)
+ return -EINVAL;
+
+ regmap_update_bits(pm4125->regmap, PM4125_ANA_MICBIAS_LDO_1_SETTING,
+ PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK, vout_ctl << 3);
+ return 0;
+}
+
+static irqreturn_t pm4125_wd_handle_irq(int irq, void *data)
+{
+ /*
+ * HPHR/HPHL Watchdog interrupt threaded handler
+ * Watchdog interrupts are expected to be enabled when switching on the HPHL/R
+ * in order to make sure the interrupts are acked by the regmap_irq handler
+ * io allow PDM sync. We could leave those interrupts masked but we would
+ * not haveany valid way to enable/disable them without violating irq layers.
+ *
+ * The HPHR/HPHL Watchdog interrupts are handled by regmap_irq, so requesting
+ * a threaded handler is the safest way to be able to ack those interrupts
+ * without colliding with the regmap_irq setup.
+ */
+ return IRQ_HANDLED;
+}
+
+static const struct irq_chip pm4125_codec_irq_chip = {
+ .name = "pm4125_codec",
+};
+
+static int pm4125_codec_irq_chip_map(struct irq_domain *irqd, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &pm4125_codec_irq_chip, handle_simple_irq);
+ irq_set_nested_thread(virq, 1);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops pm4125_domain_ops = {
+ .map = pm4125_codec_irq_chip_map,
+};
+
+static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
+{
+ pm4125->virq = irq_domain_add_linear(NULL, 1, &pm4125_domain_ops, NULL);
+ if (!(pm4125->virq)) {
+ dev_err(dev, "%s: Failed to add IRQ domain\n", __func__);
+ return -EINVAL;
+ }
+
+ pm4125_regmap_irq_chip.irq_drv_data = pm4125;
+
+ return devm_regmap_add_irq_chip(dev, pm4125->regmap, irq_create_mapping(pm4125->virq, 0),
+ IRQF_ONESHOT, 0, &pm4125_regmap_irq_chip,
+ &pm4125->irq_chip);
+}
+
+static int pm4125_soc_codec_probe(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
+ struct device *dev = component->dev;
+ unsigned long time_left;
+ int i, ret;
+
+ time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
+ msecs_to_jiffies(5000));
+ if (!time_left) {
+ dev_err(dev, "soundwire device init timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ snd_soc_component_init_regmap(component, pm4125->regmap);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0)
+ return ret;
+
+ pm4125_io_init(pm4125->regmap);
+
+ /* Set all interrupts as edge triggered */
+ for (i = 0; i < pm4125_regmap_irq_chip.num_regs; i++)
+ regmap_write(pm4125->regmap, (PM4125_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+ pm_runtime_put(dev);
+
+ pm4125->hphr_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHR_PDM_WD_INT);
+ pm4125->hphl_pdm_wd_int = regmap_irq_get_virq(pm4125->irq_chip, PM4125_IRQ_HPHL_PDM_WD_INT);
+
+ /* Request for watchdog interrupts */
+ ret = devm_request_threaded_irq(dev, pm4125->hphr_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHR PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHR wdt interrupt: %d\n", ret);
+
+ ret = devm_request_threaded_irq(dev, pm4125->hphl_pdm_wd_int, NULL, pm4125_wd_handle_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "HPHL PDM WDOG INT", pm4125);
+ if (ret)
+ dev_err(dev, "Failed to request HPHL wdt interrupt: %d\n", ret);
+
+ disable_irq_nosync(pm4125->hphr_pdm_wd_int);
+ disable_irq_nosync(pm4125->hphl_pdm_wd_int);
+
+ ret = pm4125_mbhc_init(component);
+ if (ret)
+ dev_err(component->dev, "mbhc initialization failed\n");
+
+ return ret;
+}
+
+static void pm4125_soc_codec_remove(struct snd_soc_component *component)
+{
+ struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
+
+ pm4125_mbhc_deinit(component);
+ free_irq(pm4125->hphl_pdm_wd_int, pm4125);
+ free_irq(pm4125->hphr_pdm_wd_int, pm4125);
+}
+
+static int pm4125_codec_set_jack(struct snd_soc_component *comp, struct snd_soc_jack *jack,
+ void *data)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(comp->dev);
+ int ret = 0;
+
+ if (jack)
+ ret = wcd_mbhc_start(pm4125->wcd_mbhc, &pm4125->mbhc_cfg, jack);
+ else
+ wcd_mbhc_stop(pm4125->wcd_mbhc);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_pm4125 = {
+ .name = "pm4125_codec",
+ .probe = pm4125_soc_codec_probe,
+ .remove = pm4125_soc_codec_remove,
+ .controls = pm4125_snd_controls,
+ .num_controls = ARRAY_SIZE(pm4125_snd_controls),
+ .dapm_widgets = pm4125_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm4125_dapm_widgets),
+ .dapm_routes = pm4125_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(pm4125_audio_map),
+ .set_jack = pm4125_codec_set_jack,
+ .endianness = 1,
+};
+
+static void pm4125_dt_parse_micbias_info(struct device *dev, struct pm4125_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 prop_val = 0;
+ int ret;
+
+ ret = of_property_read_u32(np, "qcom,micbias1-microvolt", &prop_val);
+ if (!ret)
+ priv->micb1_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias1 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias2-microvolt", &prop_val);
+ if (!ret)
+ priv->micb2_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias2 DT property not found\n");
+
+ ret = of_property_read_u32(np, "qcom,micbias3-microvolt", &prop_val);
+ if (!ret)
+ priv->micb3_mv = prop_val / 1000;
+ else
+ dev_warn(dev, "Micbias3 DT property not found\n");
+}
+
+static int pm4125_codec_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return pm4125_sdw_hw_params(sdw_priv, substream, params, dai);
+}
+
+static int pm4125_codec_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ return sdw_stream_remove_slave(sdw_priv->sdev, sdw_priv->sruntime);
+}
+
+static int pm4125_codec_set_sdw_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+
+ sdw_priv->sruntime = stream;
+
+ return 0;
+}
+
+static int pm4125_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dai->dev);
+ struct pm4125_sdw_priv *sdw_priv = pm4125->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n", rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n", tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = sdw_priv->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops pm4125_sdw_dai_ops = {
+ .hw_params = pm4125_codec_hw_params,
+ .hw_free = pm4125_codec_free,
+ .set_stream = pm4125_codec_set_sdw_stream,
+ .get_channel_map = pm4125_get_channel_map,
+};
+
+static struct snd_soc_dai_driver pm4125_dais[] = {
+ [0] = {
+ .name = "pm4125-sdw-rx",
+ .playback = {
+ .stream_name = "PM4125 AIF Playback",
+ .rates = PM4125_RATES | PM4125_FRAC_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 384000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+ [1] = {
+ .name = "pm4125-sdw-tx",
+ .capture = {
+ .stream_name = "PM4125 AIF Capture",
+ .rates = PM4125_RATES,
+ .formats = PM4125_FORMATS,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &pm4125_sdw_dai_ops,
+ },
+};
+
+static int pm4125_bind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+ struct device_link *devlink;
+ int ret;
+
+ /* Give the soundwire subdevices some more time to settle */
+ usleep_range(15000, 15010);
+
+ ret = component_bind_all(dev, pm4125);
+ if (ret) {
+ dev_err(dev, "Slave bind failed, ret = %d\n", ret);
+ return ret;
+ }
+
+ pm4125->rxdev = pm4125_sdw_device_get(pm4125->rxnode);
+ if (!pm4125->rxdev) {
+ dev_err(dev, "could not find rxslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_PB] = dev_get_drvdata(pm4125->rxdev);
+ pm4125->sdw_priv[AIF1_PB]->pm4125 = pm4125;
+
+ pm4125->txdev = pm4125_sdw_device_get(pm4125->txnode);
+ if (!pm4125->txdev) {
+ dev_err(dev, "could not find txslave with matching of node\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ pm4125->sdw_priv[AIF1_CAP] = dev_get_drvdata(pm4125->txdev);
+ pm4125->sdw_priv[AIF1_CAP]->pm4125 = pm4125;
+
+ pm4125->tx_sdw_dev = dev_to_sdw_dev(pm4125->txdev);
+ if (!pm4125->tx_sdw_dev) {
+ dev_err(dev, "could not get txslave with matching of dev\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ /*
+ * As TX is the main CSR reg interface, which should not be suspended first.
+ * expicilty add the dependency link
+ */
+ devlink = device_link_add(pm4125->rxdev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink TX and RX\n");
+ ret = -EINVAL;
+ goto error_unbind_all;
+ }
+
+ devlink = device_link_add(dev, pm4125->txdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and TX\n");
+ ret = -EINVAL;
+ goto link_remove_rx_tx;
+ }
+
+ devlink = device_link_add(dev, pm4125->rxdev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
+ if (!devlink) {
+ dev_err(dev, "Could not devlink PM4125 and RX\n");
+ ret = -EINVAL;
+ goto link_remove_dev_tx;
+ }
+
+ pm4125->regmap = dev_get_regmap(&pm4125->tx_sdw_dev->dev, NULL);
+ if (!pm4125->regmap) {
+ dev_err(dev, "could not get TX device regmap\n");
+ ret = -EINVAL;
+ goto link_remove_dev_rx;
+ }
+
+ ret = pm4125_irq_init(pm4125, dev);
+ if (ret) {
+ dev_err(dev, "IRQ init failed: %d\n", ret);
+ goto link_remove_dev_rx;
+ }
+
+ pm4125->sdw_priv[AIF1_PB]->slave_irq = pm4125->virq;
+ pm4125->sdw_priv[AIF1_CAP]->slave_irq = pm4125->virq;
+
+ ret = pm4125_set_micbias_data(dev, pm4125);
+ if (ret < 0) {
+ dev_err(dev, "Bad micbias pdata\n");
+ goto link_remove_dev_rx;
+ }
+
+ ret = snd_soc_register_component(dev, &soc_codec_dev_pm4125,
+ pm4125_dais, ARRAY_SIZE(pm4125_dais));
+ if (!ret)
+ return ret;
+
+ dev_err(dev, "Codec registration failed\n");
+
+link_remove_dev_rx:
+ device_link_remove(dev, pm4125->rxdev);
+link_remove_dev_tx:
+ device_link_remove(dev, pm4125->txdev);
+link_remove_rx_tx:
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+error_unbind_all:
+ component_unbind_all(dev, pm4125);
+ return ret;
+}
+
+static void pm4125_unbind(struct device *dev)
+{
+ struct pm4125_priv *pm4125 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_component(dev);
+ device_link_remove(dev, pm4125->txdev);
+ device_link_remove(dev, pm4125->rxdev);
+ device_link_remove(pm4125->rxdev, pm4125->txdev);
+ component_unbind_all(dev, pm4125);
+}
+
+static const struct component_master_ops pm4125_comp_ops = {
+ .bind = pm4125_bind,
+ .unbind = pm4125_unbind,
+};
+
+static int pm4125_add_slave_components(struct pm4125_priv *pm4125, struct device *dev,
+ struct component_match **matchptr)
+{
+ struct device_node *np = dev->of_node;
+
+ pm4125->rxnode = of_parse_phandle(np, "qcom,rx-device", 0);
+ if (!pm4125->rxnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,rx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->rxnode);
+
+ pm4125->txnode = of_parse_phandle(np, "qcom,tx-device", 0);
+ if (!pm4125->txnode)
+ return dev_err_probe(dev, -ENODEV, "Couldn't parse phandle to qcom,tx-device\n");
+ component_match_add_release(dev, matchptr, component_release_of, component_compare_of,
+ pm4125->txnode);
+
+ return 0;
+}
+
+static int pm4125_probe(struct platform_device *pdev)
+{
+ struct component_match *match = NULL;
+ struct device *dev = &pdev->dev;
+ struct pm4125_priv *pm4125;
+ struct wcd_mbhc_config *cfg;
+ int ret;
+
+ pm4125 = devm_kzalloc(dev, sizeof(*pm4125), GFP_KERNEL);
+ if (!pm4125)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, pm4125);
+
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(pm4125_power_supplies),
+ pm4125_power_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
+
+ pm4125->spmi_regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!pm4125->spmi_regmap)
+ return -ENXIO;
+
+ pm4125_reset(pm4125);
+
+ pm4125_dt_parse_micbias_info(dev, pm4125);
+ atomic_set(&pm4125->gloal_mbias_cnt, 0);
+
+ cfg = &pm4125->mbhc_cfg;
+ cfg->mbhc_micbias = MIC_BIAS_2;
+ cfg->anc_micbias = MIC_BIAS_2;
+ cfg->v_hs_max = WCD_MBHC_HS_V_MAX;
+ cfg->num_btn = PM4125_MBHC_MAX_BUTTONS;
+ cfg->micb_mv = pm4125->micb2_mv;
+ cfg->linein_th = 5000;
+ cfg->hs_thr = 1700;
+ cfg->hph_thr = 50;
+
+ wcd_dt_parse_mbhc_data(dev, &pm4125->mbhc_cfg);
+
+ ret = pm4125_add_slave_components(pm4125, dev, &match);
+ if (ret)
+ return ret;
+
+ ret = component_master_add_with_match(dev, &pm4125_comp_ops, match);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+ return 0;
+}
+
+static void pm4125_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ component_master_del(&pdev->dev, &pm4125_comp_ops);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_dont_use_autosuspend(dev);
+}
+
+static const struct of_device_id pm4125_of_match[] = {
+ { .compatible = "qcom,pm4125-codec" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pm4125_of_match);
+
+static struct platform_driver pm4125_codec_driver = {
+ .probe = pm4125_probe,
+ .remove = pm4125_remove,
+ .driver = {
+ .name = "pm4125_codec",
+ .of_match_table = pm4125_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+
+module_platform_driver(pm4125_codec_driver);
+MODULE_DESCRIPTION("PM4125 audio codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pm4125.h b/sound/soc/codecs/pm4125.h
new file mode 100644
index 000000000000..3520c711b744
--- /dev/null
+++ b/sound/soc/codecs/pm4125.h
@@ -0,0 +1,307 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#ifndef _PM4125_REGISTERS_H
+#define _PM4125_REGISTERS_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define PM4125_ANA_BASE_ADDR 0x3000
+#define PM4125_DIG_BASE_ADDR 0x3400
+
+#define PM4125_ANA_MICBIAS_MICB_1_2_EN (PM4125_ANA_BASE_ADDR+0x040)
+#define PM4125_ANA_MICBIAS_MICB1_PULL_UP_MASK BIT(5)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_UP_MASK BIT(1)
+#define PM4125_ANA_MICBIAS_MICB2_PULL_DN_MASK BIT(0)
+#define PM4125_ANA_MICBIAS_MICB_PULL_ENABLE 1
+#define PM4125_ANA_MICBIAS_MICB_PULL_DISABLE 0
+#define PM4125_ANA_MICBIAS_MICB_3_EN (PM4125_ANA_BASE_ADDR+0x041)
+#define PM4125_ANA_MICBIAS_LDO_1_SETTING (PM4125_ANA_BASE_ADDR+0x042)
+#define PM4125_ANA_MICBIAS_MICB_OUT_VAL_MASK GENMASK(7, 3)
+#define PM4125_ANA_MICBIAS_LDO_1_CTRL (PM4125_ANA_BASE_ADDR+0x043)
+#define PM4125_ANA_TX_AMIC1 (PM4125_ANA_BASE_ADDR+0x047)
+#define PM4125_ANA_TX_AMIC2 (PM4125_ANA_BASE_ADDR+0x048)
+#define PM4125_ANA_MBHC_MECH (PM4125_ANA_BASE_ADDR+0x05A)
+#define PM4125_ANA_MBHC_ELECT (PM4125_ANA_BASE_ADDR+0x05B)
+#define PM4125_ANA_MBHC_ELECT_BIAS_EN_MASK BIT(0)
+#define PM4125_ANA_MBHC_ELECT_BIAS_ENABLE 1
+#define PM4125_ANA_MBHC_ELECT_BIAS_DISABLE 0
+#define PM4125_ANA_MBHC_ZDET (PM4125_ANA_BASE_ADDR+0x05C)
+#define PM4125_ANA_MBHC_RESULT_1 (PM4125_ANA_BASE_ADDR+0x05D)
+#define PM4125_ANA_MBHC_RESULT_2 (PM4125_ANA_BASE_ADDR+0x05E)
+#define PM4125_ANA_MBHC_RESULT_3 (PM4125_ANA_BASE_ADDR+0x05F)
+#define PM4125_ANA_MBHC_BTN0_ZDET_VREF1 (PM4125_ANA_BASE_ADDR+0x060)
+#define PM4125_ANA_MBHC_BTN0_THRESHOLD_MASK GENMASK(7, 2)
+#define PM4125_ANA_MBHC_BTN1_ZDET_VREF2 (PM4125_ANA_BASE_ADDR+0x061)
+#define PM4125_ANA_MBHC_BTN2_ZDET_VREF3 (PM4125_ANA_BASE_ADDR+0x062)
+#define PM4125_ANA_MBHC_BTN3_ZDET_DBG_400 (PM4125_ANA_BASE_ADDR+0x063)
+#define PM4125_ANA_MBHC_BTN4_ZDET_DBG_1400 (PM4125_ANA_BASE_ADDR+0x064)
+#define PM4125_ANA_MBHC_MICB2_RAMP (PM4125_ANA_BASE_ADDR+0x065)
+#define PM4125_ANA_MBHC_CTL_1 (PM4125_ANA_BASE_ADDR+0x066)
+#define PM4125_ANA_MBHC_CTL_2 (PM4125_ANA_BASE_ADDR+0x067)
+#define PM4125_ANA_MBHC_PLUG_DETECT_CTL (PM4125_ANA_BASE_ADDR+0x068)
+#define PM4125_ANA_MBHC_ZDET_ANA_CTL (PM4125_ANA_BASE_ADDR+0x069)
+#define PM4125_ANA_MBHC_ZDET_RAMP_CTL (PM4125_ANA_BASE_ADDR+0x06A)
+#define PM4125_ANA_MBHC_FSM_STATUS (PM4125_ANA_BASE_ADDR+0x06B)
+#define PM4125_ANA_MBHC_ADC_RESULT (PM4125_ANA_BASE_ADDR+0x06C)
+#define PM4125_ANA_MBHC_CTL_CLK (PM4125_ANA_BASE_ADDR+0x06D)
+#define PM4125_ANA_MBHC_ZDET_CALIB_RESULT (PM4125_ANA_BASE_ADDR+0x072)
+#define PM4125_ANA_NCP_EN (PM4125_ANA_BASE_ADDR+0x077)
+#define PM4125_ANA_NCP_ENABLE_MASK BIT(0)
+#define PM4125_ANA_NCP_ENABLE 1
+#define PM4125_ANA_NCP_DISABLE 0
+#define PM4125_ANA_NCP_VCTRL (PM4125_ANA_BASE_ADDR+0x07C)
+#define PM4125_ANA_HPHPA_CNP_CTL_1 (PM4125_ANA_BASE_ADDR+0x083)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_CTL_1_EN 1
+#define PM4125_ANA_HPHPA_CNP_CTL_2 (PM4125_ANA_BASE_ADDR+0x084)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_L_MASK BIT(1)
+#define PM4125_ANA_HPHPA_CNP_OCP_EN_R_MASK BIT(0)
+#define PM4125_ANA_HPHPA_CNP_OCP_ENABLE 1
+#define PM4125_ANA_HPHPA_CNP_OCP_DISABLE 0
+#define PM4125_ANA_HPHPA_PA_STATUS (PM4125_ANA_BASE_ADDR+0x087)
+#define PM4125_ANA_HPHPA_FSM_CLK (PM4125_ANA_BASE_ADDR+0x088)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_EN_MASK BIT(7)
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_ENABLE 1
+#define PM4125_ANA_HPHPA_FSM_CLK_DIV_DISABLE 0
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_MASK GENMASK(6, 0)
+#define PM4125_ANA_HPHPA_FSM_DIV_RATIO_68 (0x11)
+#define PM4125_ANA_HPHPA_L_GAIN (PM4125_ANA_BASE_ADDR+0x08B)
+#define PM4125_ANA_HPHPA_R_GAIN (PM4125_ANA_BASE_ADDR+0x08C)
+#define PM4125_ANA_HPHPA_SPARE_CTL (PM4125_ANA_BASE_ADDR+0x08E)
+#define PM4125_SWR_HPHPA_HD2 (PM4125_ANA_BASE_ADDR+0x090)
+#define PM4125_SWR_HPHPA_HD2_LEFT_MASK GENMASK(5, 3)
+#define PM4125_SWR_HPHPA_HD2_RIGHT_MASK GENMASK(2, 0)
+#define PM4125_SWR_HPHPA_HD2_ENABLE (BIT(2) | BIT(1) | BIT(0))
+#define PM4125_ANA_SURGE_EN (PM4125_ANA_BASE_ADDR+0x097)
+#define PM4125_ANA_SURGE_PROTECTION_HPHL_MASK BIT(7)
+#define PM4125_ANA_SURGE_PROTECTION_HPHR_MASK BIT(6)
+#define PM4125_ANA_SURGE_PROTECTION_ENABLE 1
+#define PM4125_ANA_SURGE_PROTECTION_DISABLE 0
+#define PM4125_ANA_COMBOPA_CTL (PM4125_ANA_BASE_ADDR+0x09B)
+#define PM4125_ANA_COMBO_PA_SELECT_MASK BIT(6)
+#define PM4125_ANA_COMBO_PA_SELECT_EAR 0
+#define PM4125_ANA_COMBO_PA_SELECT_LO 1
+#define PM4125_ANA_COMBOPA_CTL_4 (PM4125_ANA_BASE_ADDR+0x09F)
+#define PM4125_ANA_COMBOPA_CTL_5 (PM4125_ANA_BASE_ADDR+0x0A0)
+#define PM4125_ANA_RXLDO_CTL (PM4125_ANA_BASE_ADDR+0x0B2)
+#define PM4125_ANA_MBIAS_EN (PM4125_ANA_BASE_ADDR+0x0B4)
+#define PM4125_ANA_MBIAS_EN_GLOBAL_MASK BIT(5)
+#define PM4125_ANA_MBIAS_EN_V2I_MASK BIT(4)
+#define PM4125_ANA_MBIAS_EN_ENABLE 1
+#define PM4125_ANA_MBIAS_EN_DISABLE 0
+
+#define PM4125_DIG_SWR_CHIP_ID0 (PM4125_DIG_BASE_ADDR+0x001)
+#define PM4125_DIG_SWR_CHIP_ID1 (PM4125_DIG_BASE_ADDR+0x002)
+#define PM4125_DIG_SWR_CHIP_ID2 (PM4125_DIG_BASE_ADDR+0x003)
+#define PM4125_DIG_SWR_CHIP_ID3 (PM4125_DIG_BASE_ADDR+0x004)
+#define PM4125_DIG_SWR_SWR_TX_CLK_RATE (PM4125_DIG_BASE_ADDR+0x040)
+#define PM4125_DIG_SWR_CDC_RST_CTL (PM4125_DIG_BASE_ADDR+0x041)
+#define PM4125_DIG_SWR_TOP_CLK_CFG (PM4125_DIG_BASE_ADDR+0x042)
+#define PM4125_DIG_SWR_CDC_RX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x043)
+#define PM4125_DIG_SWR_ANA_RX_DIV2_CLK_EN_MASK BIT(5)
+#define PM4125_DIG_SWR_ANA_RX_CLK_EN_MASK BIT(4)
+#define PM4125_DIG_SWR_RX1_CLK_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_RX0_CLK_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_RX_CLK_ENABLE 1
+#define PM4125_DIG_SWR_RX_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_TX_CLK_CTL (PM4125_DIG_BASE_ADDR+0x044)
+#define PM4125_DIG_SWR_SWR_RST_EN (PM4125_DIG_BASE_ADDR+0x045)
+#define PM4125_DIG_SWR_CDC_RX_RST (PM4125_DIG_BASE_ADDR+0x047)
+#define PM4125_DIG_SWR_CDC_RX0_CTL (PM4125_DIG_BASE_ADDR+0x048)
+#define PM4125_DIG_SWR_DSM_DITHER_EN_MASK BIT(7)
+#define PM4125_DIG_SWR_DSM_DITHER_DISABLE 0
+#define PM4125_DIG_SWR_DSM_DITHER_ENABLE 1
+#define PM4125_DIG_SWR_CDC_RX1_CTL (PM4125_DIG_BASE_ADDR+0x049)
+#define PM4125_DIG_SWR_CDC_TX_ANA_MODE_0_1 (PM4125_DIG_BASE_ADDR+0x04B)
+#define PM4125_DIG_SWR_TX_ANA_TXD1_MODE_MASK GENMASK(7, 4)
+#define PM4125_DIG_SWR_TX_ANA_TXD0_MODE_MASK GENMASK(3, 0)
+#define PM4125_DIG_SWR_TXD_MODE_ULPI (0x9)
+#define PM4125_DIG_SWR_TXD_MODE_NORMAL (0x3)
+#define PM4125_DIG_SWR_CDC_COMP_CTL_0 (PM4125_DIG_BASE_ADDR+0x04F)
+#define PM4125_DIG_SWR_COMP_HPHL_EN_MASK BIT(1)
+#define PM4125_DIG_SWR_COMP_HPHR_EN_MASK BIT(0)
+#define PM4125_DIG_SWR_COMP_ENABLE 1
+#define PM4125_DIG_SWR_COMP_DISABLE 0
+#define PM4125_DIG_SWR_CDC_RX_DELAY_CTL (PM4125_DIG_BASE_ADDR+0x052)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_0 (PM4125_DIG_BASE_ADDR+0x053)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_1 (PM4125_DIG_BASE_ADDR+0x054)
+#define PM4125_DIG_SWR_CDC_RX_GAIN_CTL (PM4125_DIG_BASE_ADDR+0x057)
+#define PM4125_DIG_SWR_RX1_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_RX0_EN_MASK BIT(2)
+#define PM4125_DIG_SWR_RX_INPUT_DISABLE 0
+#define PM4125_DIG_SWR_RX_INPUT_ENABLE 1
+#define PM4125_DIG_SWR_CDC_TX0_CTL (PM4125_DIG_BASE_ADDR+0x060)
+#define PM4125_DIG_SWR_CDC_TX1_CTL (PM4125_DIG_BASE_ADDR+0x061)
+#define PM4125_DIG_SWR_CDC_TX_RST (PM4125_DIG_BASE_ADDR+0x063)
+#define PM4125_DIG_SWR_CDC_REQ0_CTL (PM4125_DIG_BASE_ADDR+0x064)
+#define PM4125_DIG_SWR_CDC_REQ1_CTL (PM4125_DIG_BASE_ADDR+0x065)
+#define PM4125_DIG_SWR_CDC_RST (PM4125_DIG_BASE_ADDR+0x067)
+#define PM4125_DIG_SWR_CDC_AMIC_CTL (PM4125_DIG_BASE_ADDR+0x06A)
+#define PM4125_DIG_SWR_AMIC_SELECT_MASK BIT(1)
+#define PM4125_DIG_SWR_AMIC_SELECT_DMIC1 0
+#define PM4125_DIG_SWR_AMIC_SELECT_AMIC3 1
+#define PM4125_DIG_SWR_CDC_DMIC_CTL (PM4125_DIG_BASE_ADDR+0x06B)
+#define PM4125_DIG_SWR_CDC_DMIC1_CTL (PM4125_DIG_BASE_ADDR+0x06C)
+#define PM4125_DIG_SWR_DMIC1_CLK_EN_MASK BIT(3)
+#define PM4125_DIG_SWR_DMIC1_CLK_ENABLE 1
+#define PM4125_DIG_SWR_DMIC1_CLK_DISABLE 0
+#define PM4125_DIG_SWR_CDC_DMIC1_RATE (PM4125_DIG_BASE_ADDR+0x06D)
+#define PM4125_DIG_SWR_PDM_WD_CTL0 (PM4125_DIG_BASE_ADDR+0x070)
+#define PM4125_WDT_ENABLE_MASK GENMASK(1, 0)
+#define PM4125_WDT_ENABLE_RX0_L BIT(0)
+#define PM4125_WDT_ENABLE_RX0_M BIT(1)
+#define PM4125_DIG_SWR_PDM_WD_CTL1 (PM4125_DIG_BASE_ADDR+0x071)
+#define PM4125_WDT_ENABLE_RX1_L BIT(0)
+#define PM4125_WDT_ENABLE_RX1_M BIT(1)
+#define PM4125_DIG_SWR_INTR_MODE (PM4125_DIG_BASE_ADDR+0x080)
+#define PM4125_DIG_SWR_INTR_MASK_0 (PM4125_DIG_BASE_ADDR+0x081)
+#define PM4125_DIG_SWR_INTR_MASK_1 (PM4125_DIG_BASE_ADDR+0x082)
+#define PM4125_DIG_SWR_INTR_MASK_2 (PM4125_DIG_BASE_ADDR+0x083)
+#define PM4125_DIG_SWR_INTR_STATUS_0 (PM4125_DIG_BASE_ADDR+0x084)
+#define PM4125_DIG_SWR_INTR_STATUS_1 (PM4125_DIG_BASE_ADDR+0x085)
+#define PM4125_DIG_SWR_INTR_STATUS_2 (PM4125_DIG_BASE_ADDR+0x086)
+#define PM4125_DIG_SWR_INTR_CLEAR_0 (PM4125_DIG_BASE_ADDR+0x087)
+#define PM4125_DIG_SWR_INTR_CLEAR_1 (PM4125_DIG_BASE_ADDR+0x088)
+#define PM4125_DIG_SWR_INTR_CLEAR_2 (PM4125_DIG_BASE_ADDR+0x089)
+#define PM4125_DIG_SWR_INTR_LEVEL_0 (PM4125_DIG_BASE_ADDR+0x08A)
+#define PM4125_DIG_SWR_INTR_LEVEL_1 (PM4125_DIG_BASE_ADDR+0x08B)
+#define PM4125_DIG_SWR_INTR_LEVEL_2 (PM4125_DIG_BASE_ADDR+0x08C)
+#define PM4125_DIG_SWR_CDC_CONN_RX0_CTL (PM4125_DIG_BASE_ADDR+0x093)
+#define PM4125_DIG_SWR_CDC_CONN_RX1_CTL (PM4125_DIG_BASE_ADDR+0x094)
+#define PM4125_DIG_SWR_LOOP_BACK_MODE (PM4125_DIG_BASE_ADDR+0x097)
+#define PM4125_DIG_SWR_DRIVE_STRENGTH_0 (PM4125_DIG_BASE_ADDR+0x0A0)
+#define PM4125_DIG_SWR_DIG_DEBUG_CTL (PM4125_DIG_BASE_ADDR+0x0AB)
+#define PM4125_DIG_SWR_DIG_DEBUG_EN (PM4125_DIG_BASE_ADDR+0x0AC)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA0 (PM4125_DIG_BASE_ADDR+0x0B0)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA1 (PM4125_DIG_BASE_ADDR+0x0B1)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA2 (PM4125_DIG_BASE_ADDR+0x0B2)
+#define PM4125_DIG_SWR_DEM_BYPASS_DATA3 (PM4125_DIG_BASE_ADDR+0x0B3)
+
+#define PM4125_ANALOG_REGISTERS_MAX_SIZE (PM4125_ANA_BASE_ADDR+0x0B5)
+#define PM4125_DIGITAL_REGISTERS_MAX_SIZE (PM4125_DIG_BASE_ADDR+0x0B4)
+#define PM4125_ANALOG_MAX_REGISTER (PM4125_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define PM4125_DIGITAL_MAX_REGISTER (PM4125_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#define PM4125_MAX_REGISTER PM4125_DIGITAL_MAX_REGISTER
+
+#define PM4125_MAX_MICBIAS 3
+#define PM4125_MAX_SWR_CH_IDS 15
+#define PM4125_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
+
+enum pm4125_tx_sdw_ports {
+ PM4125_ADC_1_2_DMIC1L_BCS_PORT = 1,
+ PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+ PM4125_MAX_TX_SWR_PORTS = PM4125_DMIC_1L_1R_ADC1_BCS_PORT,
+};
+
+enum pm4125_rx_sdw_ports {
+ PM4125_HPH_PORT = 1,
+ PM4125_COMP_PORT,
+ PM4125_MAX_SWR_PORTS = PM4125_COMP_PORT,
+};
+
+struct pm4125_sdw_ch_info {
+ int port_num;
+ unsigned int ch_mask;
+ unsigned int master_ch_mask;
+};
+
+#define WCD_SDW_CH(id, pn, cmask) \
+ [id] = { \
+ .port_num = pn, \
+ .ch_mask = cmask, \
+ .master_ch_mask = cmask, \
+ }
+
+struct pm4125_priv;
+struct pm4125_sdw_priv {
+ struct sdw_slave *sdev;
+ struct sdw_stream_config sconfig;
+ struct sdw_stream_runtime *sruntime;
+ struct sdw_port_config port_config[PM4125_MAX_SWR_PORTS];
+ struct pm4125_sdw_ch_info *ch_info;
+ bool port_enable[PM4125_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
+ int active_ports;
+ int num_ports;
+ bool is_tx;
+ struct pm4125_priv *pm4125;
+ struct irq_domain *slave_irq;
+ struct regmap *regmap;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_PM4125_SDW)
+int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125, struct snd_soc_dai *dai, void *stream,
+ int direction);
+int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125, struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai);
+
+struct device *pm4125_sdw_device_get(struct device_node *np);
+
+#else
+static inline int pm4125_sdw_free(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_set_sdw_stream(struct pm4125_sdw_priv *pm4125,
+ struct snd_soc_dai *dai, void *stream, int direction)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int pm4125_sdw_hw_params(struct pm4125_sdw_priv *pm4125,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ return -EOPNOTSUPP;
+}
+#endif
+
+enum {
+ /* INTR_CTRL_INT_MASK_0 */
+ PM4125_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+ PM4125_IRQ_MBHC_BUTTON_RELEASE_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_DET,
+ PM4125_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+ PM4125_IRQ_MBHC_SW_DET,
+ PM4125_IRQ_HPHR_OCP_INT,
+ PM4125_IRQ_HPHR_CNP_INT,
+ PM4125_IRQ_HPHL_OCP_INT,
+
+ /* INTR_CTRL_INT_MASK_1 */
+ PM4125_IRQ_HPHL_CNP_INT,
+ PM4125_IRQ_EAR_CNP_INT,
+ PM4125_IRQ_EAR_SCD_INT,
+ PM4125_IRQ_AUX_CNP_INT,
+ PM4125_IRQ_AUX_SCD_INT,
+ PM4125_IRQ_HPHL_PDM_WD_INT,
+ PM4125_IRQ_HPHR_PDM_WD_INT,
+ PM4125_IRQ_AUX_PDM_WD_INT,
+
+ /* INTR_CTRL_INT_MASK_2 */
+ PM4125_IRQ_LDORT_SCD_INT,
+ PM4125_IRQ_MBHC_MOISTURE_INT,
+ PM4125_IRQ_HPHL_SURGE_DET_INT,
+ PM4125_IRQ_HPHR_SURGE_DET_INT,
+ PM4125_NUM_IRQS,
+};
+
+enum pm4125_tx_sdw_channels {
+ PM4125_ADC1,
+ PM4125_ADC2,
+};
+
+enum pm4125_rx_sdw_channels {
+ PM4125_HPH_L,
+ PM4125_HPH_R,
+ PM4125_COMP_L,
+ PM4125_COMP_R,
+};
+
+#endif /* _PM4125_REGISTERS_H */
diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c
new file mode 100644
index 000000000000..8b51e87a1711
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.c
@@ -0,0 +1,974 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip RK3308 internal audio codec driver
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2024, Vivax-Metrotech Ltd
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/util_macros.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "rk3308_codec.h"
+
+#define ADC_LR_GROUP_MAX 4
+
+#define GRF_CHIP_ID 0x800
+
+enum {
+ ACODEC_VERSION_A = 'A',
+ ACODEC_VERSION_B,
+ ACODEC_VERSION_C,
+};
+
+struct rk3308_codec_priv {
+ const struct device *dev;
+ struct regmap *regmap;
+ struct regmap *grf;
+ struct reset_control *reset;
+ struct clk *hclk;
+ struct clk *mclk_rx;
+ struct clk *mclk_tx;
+ struct snd_soc_component *component;
+ unsigned char codec_ver;
+};
+
+static struct clk_bulk_data rk3308_codec_clocks[] = {
+ { .id = "hclk" },
+ { .id = "mclk_rx" },
+ { .id = "mclk_tx" },
+};
+
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_adc_alc_gain_tlv, -1800, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpout_gain_tlv, -3900, 150, 0);
+static const DECLARE_TLV_DB_SCALE(rk3308_codec_dac_hpmix_gain_tlv, -600, 600, 0);
+
+static const DECLARE_TLV_DB_RANGE(rk3308_codec_dac_lineout_gain_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-600, 0, 0),
+ 1, 1, TLV_DB_SCALE_ITEM(-300, 0, 0),
+ 2, 2, TLV_DB_SCALE_ITEM(-150, 0, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(0, 0, 0),
+);
+
+static const char * const rk3308_codec_hpf_cutoff_text[] = {
+ "20 Hz", "245 Hz", "612 Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum12, RK3308_ADC_DIG_CON04(0), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum34, RK3308_ADC_DIG_CON04(1), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum56, RK3308_ADC_DIG_CON04(2), 0,
+ rk3308_codec_hpf_cutoff_text);
+static SOC_ENUM_SINGLE_DECL(rk3308_codec_hpf_cutoff_enum78, RK3308_ADC_DIG_CON04(3), 0,
+ rk3308_codec_hpf_cutoff_text);
+
+static const struct snd_kcontrol_new rk3308_codec_controls[] = {
+ /* Despite the register names, these set the gain when AGC is OFF */
+ SOC_SINGLE_RANGE_TLV("MIC1 Capture Volume",
+ RK3308_ADC_ANA_CON03(0),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC2 Capture Volume",
+ RK3308_ADC_ANA_CON04(0),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC3 Capture Volume",
+ RK3308_ADC_ANA_CON03(1),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC4 Capture Volume",
+ RK3308_ADC_ANA_CON04(1),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC5 Capture Volume",
+ RK3308_ADC_ANA_CON03(2),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC6 Capture Volume",
+ RK3308_ADC_ANA_CON04(2),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC7 Capture Volume",
+ RK3308_ADC_ANA_CON03(3),
+ RK3308_ADC_CH1_ALC_GAIN_SFT,
+ RK3308_ADC_CH1_ALC_GAIN_MIN,
+ RK3308_ADC_CH1_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+ SOC_SINGLE_RANGE_TLV("MIC8 Capture Volume",
+ RK3308_ADC_ANA_CON04(3),
+ RK3308_ADC_CH2_ALC_GAIN_SFT,
+ RK3308_ADC_CH2_ALC_GAIN_MIN,
+ RK3308_ADC_CH2_ALC_GAIN_MAX,
+ 0, rk3308_codec_adc_alc_gain_tlv),
+
+ SOC_SINGLE("MIC1 Capture Switch", RK3308_ADC_ANA_CON00(0), 3, 1, 0),
+ SOC_SINGLE("MIC2 Capture Switch", RK3308_ADC_ANA_CON00(0), 7, 1, 0),
+ SOC_SINGLE("MIC3 Capture Switch", RK3308_ADC_ANA_CON00(1), 3, 1, 0),
+ SOC_SINGLE("MIC4 Capture Switch", RK3308_ADC_ANA_CON00(1), 7, 1, 0),
+ SOC_SINGLE("MIC5 Capture Switch", RK3308_ADC_ANA_CON00(2), 3, 1, 0),
+ SOC_SINGLE("MIC6 Capture Switch", RK3308_ADC_ANA_CON00(2), 7, 1, 0),
+ SOC_SINGLE("MIC7 Capture Switch", RK3308_ADC_ANA_CON00(3), 3, 1, 0),
+ SOC_SINGLE("MIC8 Capture Switch", RK3308_ADC_ANA_CON00(3), 7, 1, 0),
+
+ SOC_SINGLE("MIC12 HPF Capture Switch", RK3308_ADC_DIG_CON04(0), 2, 1, 1),
+ SOC_SINGLE("MIC34 HPF Capture Switch", RK3308_ADC_DIG_CON04(1), 2, 1, 1),
+ SOC_SINGLE("MIC56 HPF Capture Switch", RK3308_ADC_DIG_CON04(2), 2, 1, 1),
+ SOC_SINGLE("MIC78 HPF Capture Switch", RK3308_ADC_DIG_CON04(3), 2, 1, 1),
+
+ SOC_ENUM("MIC12 HPF Cutoff", rk3308_codec_hpf_cutoff_enum12),
+ SOC_ENUM("MIC34 HPF Cutoff", rk3308_codec_hpf_cutoff_enum34),
+ SOC_ENUM("MIC56 HPF Cutoff", rk3308_codec_hpf_cutoff_enum56),
+ SOC_ENUM("MIC78 HPF Cutoff", rk3308_codec_hpf_cutoff_enum78),
+
+ SOC_DOUBLE_TLV("Line Out Playback Volume",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_GAIN_SFT,
+ RK3308_DAC_R_LINEOUT_GAIN_SFT,
+ RK3308_DAC_x_LINEOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_lineout_gain_tlv),
+ SOC_DOUBLE("Line Out Playback Switch",
+ RK3308_DAC_ANA_CON04,
+ RK3308_DAC_L_LINEOUT_MUTE_SFT,
+ RK3308_DAC_R_LINEOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+ RK3308_DAC_ANA_CON05,
+ RK3308_DAC_ANA_CON06,
+ RK3308_DAC_x_HPOUT_GAIN_SFT,
+ RK3308_DAC_x_HPOUT_GAIN_MAX,
+ 0, rk3308_codec_dac_hpout_gain_tlv),
+ SOC_DOUBLE("Headphone Playback Switch",
+ RK3308_DAC_ANA_CON03,
+ RK3308_DAC_L_HPOUT_MUTE_SFT,
+ RK3308_DAC_R_HPOUT_MUTE_SFT, 1, 0),
+ SOC_DOUBLE_RANGE_TLV("DAC HPMIX Playback Volume",
+ RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_SFT,
+ RK3308_DAC_R_HPMIX_GAIN_SFT,
+ 1, 2, 0, rk3308_codec_dac_hpmix_gain_tlv),
+};
+
+static int rk3308_codec_pop_sound_set(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ unsigned int val = (event == SND_SOC_DAPM_POST_PMU) ?
+ RK3308_DAC_HPOUT_POP_SOUND_x_WORK :
+ RK3308_DAC_HPOUT_POP_SOUND_x_INIT;
+ unsigned int mask = RK3308_DAC_HPOUT_POP_SOUND_x_MSK;
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON01,
+ mask << w->shift, val << w->shift);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rk3308_codec_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_INPUT("MIC5"),
+ SND_SOC_DAPM_INPUT("MIC6"),
+ SND_SOC_DAPM_INPUT("MIC7"),
+ SND_SOC_DAPM_INPUT("MIC8"),
+
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN12", RK3308_ADC_ANA_CON06(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN34", RK3308_ADC_ANA_CON06(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN56", RK3308_ADC_ANA_CON06(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC_CURRENT_EN78", RK3308_ADC_ANA_CON06(3), 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_EN", RK3308_ADC_ANA_CON00(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_EN", RK3308_ADC_ANA_CON00(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_EN", RK3308_ADC_ANA_CON00(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_EN", RK3308_ADC_ANA_CON00(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_EN", RK3308_ADC_ANA_CON00(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_EN", RK3308_ADC_ANA_CON00(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_EN", RK3308_ADC_ANA_CON00(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_EN", RK3308_ADC_ANA_CON00(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC1_WORK", RK3308_ADC_ANA_CON00(0), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC2_WORK", RK3308_ADC_ANA_CON00(0), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC3_WORK", RK3308_ADC_ANA_CON00(1), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC4_WORK", RK3308_ADC_ANA_CON00(1), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC5_WORK", RK3308_ADC_ANA_CON00(2), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC6_WORK", RK3308_ADC_ANA_CON00(2), 6, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC7_WORK", RK3308_ADC_ANA_CON00(3), 2, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_mic, "MIC8_WORK", RK3308_ADC_ANA_CON00(3), 6, 1, 1, 0),
+
+ /*
+ * In theory MIC1 and MIC2 can switch to LINE IN, but this is not
+ * supported so all we can do is enabling the MIC input.
+ */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH1_IN_SEL", RK3308_ADC_ANA_CON07(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "CH2_IN_SEL", RK3308_ADC_ANA_CON07(0), 6, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_BUF_REF_EN", RK3308_ADC_ANA_CON00(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_BUF_REF_EN", RK3308_ADC_ANA_CON00(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_BUF_REF_EN", RK3308_ADC_ANA_CON00(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_BUF_REF_EN", RK3308_ADC_ANA_CON00(3), 4, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC_MCLK_GATE", RK3308_GLB_CON, 5, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ADC1_CLK_EN", RK3308_ADC_ANA_CON05(0), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC2_CLK_EN", RK3308_ADC_ANA_CON05(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC3_CLK_EN", RK3308_ADC_ANA_CON05(1), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC4_CLK_EN", RK3308_ADC_ANA_CON05(1), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC5_CLK_EN", RK3308_ADC_ANA_CON05(2), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC6_CLK_EN", RK3308_ADC_ANA_CON05(2), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC7_CLK_EN", RK3308_ADC_ANA_CON05(3), 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC8_CLK_EN", RK3308_ADC_ANA_CON05(3), 4, 0, NULL, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_EN", RK3308_ADC_ANA_CON02(0), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_EN", RK3308_ADC_ANA_CON02(0), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_EN", RK3308_ADC_ANA_CON02(1), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_EN", RK3308_ADC_ANA_CON02(1), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_EN", RK3308_ADC_ANA_CON02(2), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_EN", RK3308_ADC_ANA_CON02(2), 4, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_EN", RK3308_ADC_ANA_CON02(3), 0, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_EN", RK3308_ADC_ANA_CON02(3), 4, 1, 1, 0),
+
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC1_EN", RK3308_ADC_ANA_CON05(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC2_EN", RK3308_ADC_ANA_CON05(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC3_EN", RK3308_ADC_ANA_CON05(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC4_EN", RK3308_ADC_ANA_CON05(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC5_EN", RK3308_ADC_ANA_CON05(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC6_EN", RK3308_ADC_ANA_CON05(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC7_EN", RK3308_ADC_ANA_CON05(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ADC8_EN", RK3308_ADC_ANA_CON05(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_ADC("ADC1_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 2, 0),
+ SND_SOC_DAPM_ADC("ADC2_WORK", "Capture", RK3308_ADC_ANA_CON05(0), 6, 0),
+ SND_SOC_DAPM_ADC("ADC3_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 2, 0),
+ SND_SOC_DAPM_ADC("ADC4_WORK", "Capture", RK3308_ADC_ANA_CON05(1), 6, 0),
+ SND_SOC_DAPM_ADC("ADC5_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 2, 0),
+ SND_SOC_DAPM_ADC("ADC6_WORK", "Capture", RK3308_ADC_ANA_CON05(2), 6, 0),
+ SND_SOC_DAPM_ADC("ADC7_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 2, 0),
+ SND_SOC_DAPM_ADC("ADC8_WORK", "Capture", RK3308_ADC_ANA_CON05(3), 6, 0),
+
+ /* The "ALC" name from the TRM is misleading, these are needed even without ALC/AGC */
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC1_WORK", RK3308_ADC_ANA_CON02(0), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC2_WORK", RK3308_ADC_ANA_CON02(0), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC3_WORK", RK3308_ADC_ANA_CON02(1), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC4_WORK", RK3308_ADC_ANA_CON02(1), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC5_WORK", RK3308_ADC_ANA_CON02(2), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC6_WORK", RK3308_ADC_ANA_CON02(2), 5, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC7_WORK", RK3308_ADC_ANA_CON02(3), 1, 1, 1, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_adc, "ALC8_WORK", RK3308_ADC_ANA_CON02(3), 5, 1, 1, 0),
+
+ SND_SOC_DAPM_SUPPLY("MICBIAS Current", RK3308_ADC_ANA_CON08(0), 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", RK3308_ADC_ANA_CON07(1), 3, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", RK3308_ADC_ANA_CON07(2), 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_EN", RK3308_DAC_ANA_CON13, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_EN", RK3308_DAC_ANA_CON13, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_WORK", RK3308_DAC_ANA_CON13, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_WORK", RK3308_DAC_ANA_CON13, 5, 0, NULL, 0),
+ /* HPMIX is not actually acting as a mixer as the only supported input is I2S */
+ SND_SOC_DAPM_OUT_DRV("DAC_L_HPMIX_SEL", RK3308_DAC_ANA_CON12, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("DAC_R_HPMIX_SEL", RK3308_DAC_ANA_CON12, 6, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Left", RK3308_DAC_ANA_CON13, 2, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("DAC HPMIX Right", RK3308_DAC_ANA_CON13, 6, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_MCLK_GATE", RK3308_GLB_CON, 4, 1, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_CURRENT_EN", RK3308_DAC_ANA_CON00, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_REF_EN", RK3308_DAC_ANA_CON02, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_REF_EN", RK3308_DAC_ANA_CON02, 4, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_L_CLK_EN", RK3308_DAC_ANA_CON02, 1, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_R_CLK_EN", RK3308_DAC_ANA_CON02, 5, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DAC_L_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 3, 0),
+ SND_SOC_DAPM_DAC("DAC_R_DAC_WORK", NULL, RK3308_DAC_ANA_CON02, 7, 0),
+
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_L", RK3308_DAC_ANA_CON01, 2, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC_BUF_REF_R", RK3308_DAC_ANA_CON01, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_L", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("HPOUT_POP_SOUND_R", SND_SOC_NOPM, 4, 0, NULL, 0,
+ rk3308_codec_pop_sound_set,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_EN", RK3308_DAC_ANA_CON03, 1, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_EN", RK3308_DAC_ANA_CON03, 5, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("L_HPOUT_WORK", RK3308_DAC_ANA_CON03, 2, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_HPOUT_WORK", RK3308_DAC_ANA_CON03, 6, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HPOUT_L"),
+ SND_SOC_DAPM_OUTPUT("HPOUT_R"),
+
+ SND_SOC_DAPM_OUT_DRV("L_LINEOUT_EN", RK3308_DAC_ANA_CON04, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUT_DRV("R_LINEOUT_EN", RK3308_DAC_ANA_CON04, 4, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_L"),
+ SND_SOC_DAPM_OUTPUT("LINEOUT_R"),
+};
+
+static const struct snd_soc_dapm_route rk3308_codec_dapm_routes[] = {
+ { "MICBIAS1", NULL, "MICBIAS Current" },
+ { "MICBIAS2", NULL, "MICBIAS Current" },
+
+ { "MIC1_EN", NULL, "MIC1" },
+ { "MIC2_EN", NULL, "MIC2" },
+ { "MIC3_EN", NULL, "MIC3" },
+ { "MIC4_EN", NULL, "MIC4" },
+ { "MIC5_EN", NULL, "MIC5" },
+ { "MIC6_EN", NULL, "MIC6" },
+ { "MIC7_EN", NULL, "MIC7" },
+ { "MIC8_EN", NULL, "MIC8" },
+
+ { "MIC1_WORK", NULL, "MIC1_EN" },
+ { "MIC2_WORK", NULL, "MIC2_EN" },
+ { "MIC3_WORK", NULL, "MIC3_EN" },
+ { "MIC4_WORK", NULL, "MIC4_EN" },
+ { "MIC5_WORK", NULL, "MIC5_EN" },
+ { "MIC6_WORK", NULL, "MIC6_EN" },
+ { "MIC7_WORK", NULL, "MIC7_EN" },
+ { "MIC8_WORK", NULL, "MIC8_EN" },
+
+ { "CH1_IN_SEL", NULL, "MIC1_WORK" },
+ { "CH2_IN_SEL", NULL, "MIC2_WORK" },
+
+ { "ALC1_EN", NULL, "CH1_IN_SEL" },
+ { "ALC2_EN", NULL, "CH2_IN_SEL" },
+ { "ALC3_EN", NULL, "MIC3_WORK" },
+ { "ALC4_EN", NULL, "MIC4_WORK" },
+ { "ALC5_EN", NULL, "MIC5_WORK" },
+ { "ALC6_EN", NULL, "MIC6_WORK" },
+ { "ALC7_EN", NULL, "MIC7_WORK" },
+ { "ALC8_EN", NULL, "MIC8_WORK" },
+
+ { "ADC1_EN", NULL, "ALC1_EN" },
+ { "ADC2_EN", NULL, "ALC2_EN" },
+ { "ADC3_EN", NULL, "ALC3_EN" },
+ { "ADC4_EN", NULL, "ALC4_EN" },
+ { "ADC5_EN", NULL, "ALC5_EN" },
+ { "ADC6_EN", NULL, "ALC6_EN" },
+ { "ADC7_EN", NULL, "ALC7_EN" },
+ { "ADC8_EN", NULL, "ALC8_EN" },
+
+ { "ADC1_WORK", NULL, "ADC1_EN" },
+ { "ADC2_WORK", NULL, "ADC2_EN" },
+ { "ADC3_WORK", NULL, "ADC3_EN" },
+ { "ADC4_WORK", NULL, "ADC4_EN" },
+ { "ADC5_WORK", NULL, "ADC5_EN" },
+ { "ADC6_WORK", NULL, "ADC6_EN" },
+ { "ADC7_WORK", NULL, "ADC7_EN" },
+ { "ADC8_WORK", NULL, "ADC8_EN" },
+
+ { "ADC1_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC2_BUF_REF_EN", NULL, "ADC_CURRENT_EN12" },
+ { "ADC3_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC4_BUF_REF_EN", NULL, "ADC_CURRENT_EN34" },
+ { "ADC5_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC6_BUF_REF_EN", NULL, "ADC_CURRENT_EN56" },
+ { "ADC7_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+ { "ADC8_BUF_REF_EN", NULL, "ADC_CURRENT_EN78" },
+
+ { "ADC1_WORK", NULL, "ADC1_BUF_REF_EN" },
+ { "ADC2_WORK", NULL, "ADC2_BUF_REF_EN" },
+ { "ADC3_WORK", NULL, "ADC3_BUF_REF_EN" },
+ { "ADC4_WORK", NULL, "ADC4_BUF_REF_EN" },
+ { "ADC5_WORK", NULL, "ADC5_BUF_REF_EN" },
+ { "ADC6_WORK", NULL, "ADC6_BUF_REF_EN" },
+ { "ADC7_WORK", NULL, "ADC7_BUF_REF_EN" },
+ { "ADC8_WORK", NULL, "ADC8_BUF_REF_EN" },
+
+ { "ADC1_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC2_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC3_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC4_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC5_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC6_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC7_CLK_EN", NULL, "ADC_MCLK_GATE" },
+ { "ADC8_CLK_EN", NULL, "ADC_MCLK_GATE" },
+
+ { "ADC1_WORK", NULL, "ADC1_CLK_EN" },
+ { "ADC2_WORK", NULL, "ADC2_CLK_EN" },
+ { "ADC3_WORK", NULL, "ADC3_CLK_EN" },
+ { "ADC4_WORK", NULL, "ADC4_CLK_EN" },
+ { "ADC5_WORK", NULL, "ADC5_CLK_EN" },
+ { "ADC6_WORK", NULL, "ADC6_CLK_EN" },
+ { "ADC7_WORK", NULL, "ADC7_CLK_EN" },
+ { "ADC8_WORK", NULL, "ADC8_CLK_EN" },
+
+ { "ALC1_WORK", NULL, "ADC1_WORK" },
+ { "ALC2_WORK", NULL, "ADC2_WORK" },
+ { "ALC3_WORK", NULL, "ADC3_WORK" },
+ { "ALC4_WORK", NULL, "ADC4_WORK" },
+ { "ALC5_WORK", NULL, "ADC5_WORK" },
+ { "ALC6_WORK", NULL, "ADC6_WORK" },
+ { "ALC7_WORK", NULL, "ADC7_WORK" },
+ { "ALC8_WORK", NULL, "ADC8_WORK" },
+
+ { "HiFi Capture", NULL, "ALC1_WORK" },
+ { "HiFi Capture", NULL, "ALC2_WORK" },
+ { "HiFi Capture", NULL, "ALC3_WORK" },
+ { "HiFi Capture", NULL, "ALC4_WORK" },
+ { "HiFi Capture", NULL, "ALC5_WORK" },
+ { "HiFi Capture", NULL, "ALC6_WORK" },
+ { "HiFi Capture", NULL, "ALC7_WORK" },
+ { "HiFi Capture", NULL, "ALC8_WORK" },
+
+ { "DAC_L_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_R_HPMIX_EN", NULL, "HiFi Playback" },
+ { "DAC_L_HPMIX_WORK", NULL, "DAC_L_HPMIX_EN" },
+ { "DAC_R_HPMIX_WORK", NULL, "DAC_R_HPMIX_EN" },
+ { "DAC HPMIX Left", NULL, "DAC_L_HPMIX_WORK" },
+ { "DAC HPMIX Right", NULL, "DAC_R_HPMIX_WORK" },
+
+ { "DAC_L_DAC_WORK", NULL, "DAC HPMIX Left" },
+ { "DAC_R_DAC_WORK", NULL, "DAC HPMIX Right" },
+
+ { "DAC_L_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_R_REF_EN", NULL, "DAC_CURRENT_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_L_REF_EN" },
+ { "DAC_R_CLK_EN", NULL, "DAC_R_REF_EN" },
+ { "DAC_L_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_R_CLK_EN", NULL, "DAC_MCLK_GATE" },
+ { "DAC_L_DAC_WORK", NULL, "DAC_L_CLK_EN" },
+ { "DAC_R_DAC_WORK", NULL, "DAC_R_CLK_EN" },
+ { "DAC_L_HPMIX_SEL", NULL, "DAC_L_DAC_WORK" },
+ { "DAC_R_HPMIX_SEL", NULL, "DAC_R_DAC_WORK" },
+
+ { "HPOUT_L", NULL, "DAC_BUF_REF_L" },
+ { "HPOUT_R", NULL, "DAC_BUF_REF_R" },
+ { "L_HPOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_HPOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "L_HPOUT_WORK", NULL, "L_HPOUT_EN" },
+ { "R_HPOUT_WORK", NULL, "R_HPOUT_EN" },
+ { "HPOUT_POP_SOUND_L", NULL, "L_HPOUT_WORK" },
+ { "HPOUT_POP_SOUND_R", NULL, "R_HPOUT_WORK" },
+ { "HPOUT_L", NULL, "HPOUT_POP_SOUND_L" },
+ { "HPOUT_R", NULL, "HPOUT_POP_SOUND_R" },
+
+ { "L_LINEOUT_EN", NULL, "DAC_L_HPMIX_SEL" },
+ { "R_LINEOUT_EN", NULL, "DAC_R_HPMIX_SEL" },
+ { "LINEOUT_L", NULL, "L_LINEOUT_EN" },
+ { "LINEOUT_R", NULL, "R_LINEOUT_EN" },
+};
+
+static int rk3308_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
+ unsigned int fmt)
+{
+ struct snd_soc_component *component = codec_dai->component;
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+ const unsigned int inv_bits = fmt & SND_SOC_DAIFMT_INV_MASK;
+ const bool inv_bitclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_IB_NF);
+ const bool inv_frmclk =
+ (inv_bits & SND_SOC_DAIFMT_IB_IF) ||
+ (inv_bits & SND_SOC_DAIFMT_NB_IF);
+ const unsigned int dac_master_bits = rk3308->codec_ver < ACODEC_VERSION_C ?
+ RK3308_DAC_IO_MODE_MASTER | RK3308_DAC_MODE_MASTER :
+ RK3308BS_DAC_IO_MODE_MASTER | RK3308BS_DAC_MODE_MASTER;
+ unsigned int adc_aif1 = 0, adc_aif2 = 0, dac_aif1 = 0, dac_aif2 = 0;
+ bool is_master = false;
+ int grp;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ adc_aif2 |= RK3308_ADC_IO_MODE_MASTER;
+ adc_aif2 |= RK3308_ADC_MODE_MASTER;
+ dac_aif2 |= dac_master_bits;
+ is_master = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_PCM;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_PCM;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_I2S;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_RJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_RJ;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ adc_aif1 |= RK3308_ADC_I2S_MODE_LJ;
+ dac_aif1 |= RK3308_DAC_I2S_MODE_LJ;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (inv_bitclk) {
+ adc_aif2 |= RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL;
+ dac_aif2 |= RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL;
+ }
+
+ if (inv_frmclk) {
+ adc_aif1 |= RK3308_ADC_I2S_LRC_POL_REVERSAL;
+ dac_aif1 |= RK3308_DAC_I2S_LRC_POL_REVERSAL;
+ }
+
+ /*
+ * Hold ADC Digital registers start at master mode
+ *
+ * There are 8 ADCs which use the same internal SCLK and LRCK for
+ * master mode. We need to make sure that they are in effect at the
+ * same time, otherwise they will cause abnormal clocks.
+ */
+ if (is_master)
+ regmap_clear_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_LRC_POL_REVERSAL |
+ RK3308_ADC_I2S_MODE_MSK,
+ adc_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp),
+ RK3308_ADC_IO_MODE_MASTER |
+ RK3308_ADC_MODE_MASTER |
+ RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL,
+ adc_aif2);
+ }
+
+ /* Hold ADC Digital registers end at master mode */
+ if (is_master)
+ regmap_set_bits(rk3308->regmap, RK3308_GLB_CON, RK3308_ADC_DIG_WORK);
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_LRC_POL_REVERSAL |
+ RK3308_DAC_I2S_MODE_MSK,
+ dac_aif1);
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON02,
+ dac_master_bits | RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL,
+ dac_aif2);
+
+ return 0;
+}
+
+static int rk3308_codec_dac_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int dac_aif1 = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dac_aif1 |= RK3308_DAC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_DIG_CON01,
+ RK3308_DAC_I2S_VALID_LEN_MSK, dac_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_DIG_CON02, RK3308_DAC_I2S_WORK);
+
+ return 0;
+}
+
+static int rk3308_codec_adc_dig_config(struct rk3308_codec_priv *rk3308,
+ struct snd_pcm_hw_params *params)
+{
+ unsigned int adc_aif1 = 0;
+ /*
+ * grp 0 = ADC1 and ADC2
+ * grp 1 = ADC3 and ADC4
+ * grp 2 = ADC5 and ADC6
+ * grp 3 = ADC7 and ADC8
+ */
+ u32 used_adc_grps;
+ int grp;
+
+ switch (params_channels(params)) {
+ case 1:
+ adc_aif1 |= RK3308_ADC_I2S_MONO;
+ used_adc_grps = 1;
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ used_adc_grps = params_channels(params) / 2;
+ break;
+ default:
+ dev_err(rk3308->dev, "Invalid channel number %d\n", params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_20BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ adc_aif1 |= RK3308_ADC_I2S_VALID_LEN_32BITS;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (grp = 0; grp < used_adc_grps; grp++) {
+ regmap_update_bits(rk3308->regmap,
+ RK3308_ADC_DIG_CON03(grp),
+ RK3308_ADC_L_CH_BIST_MSK | RK3308_ADC_R_CH_BIST_MSK,
+ RK3308_ADC_L_CH_NORMAL_LEFT | RK3308_ADC_R_CH_NORMAL_RIGHT);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_DIG_CON01(grp),
+ RK3308_ADC_I2S_VALID_LEN_MSK | RK3308_ADC_I2S_MONO, adc_aif1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_DIG_CON02(grp), RK3308_ADC_I2S_WORK);
+ }
+
+ return 0;
+}
+
+static int rk3308_codec_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 rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ return (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ rk3308_codec_dac_dig_config(rk3308, params) :
+ rk3308_codec_adc_dig_config(rk3308, params);
+}
+
+static const struct snd_soc_dai_ops rk3308_codec_dai_ops = {
+ .hw_params = rk3308_codec_hw_params,
+ .set_fmt = rk3308_codec_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver rk3308_codec_dai_driver = {
+ .name = "rk3308-hifi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .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 = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE),
+ },
+ .ops = &rk3308_codec_dai_ops,
+};
+
+static void rk3308_codec_reset(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ reset_control_assert(rk3308->reset);
+ usleep_range(10000, 11000); /* estimated value */
+ reset_control_deassert(rk3308->reset);
+
+ regmap_write(rk3308->regmap, RK3308_GLB_CON, 0x00);
+ usleep_range(10000, 11000); /* estimated value */
+ regmap_write(rk3308->regmap, RK3308_GLB_CON,
+ RK3308_SYS_WORK |
+ RK3308_DAC_DIG_WORK |
+ RK3308_ADC_DIG_WORK);
+}
+
+/*
+ * Initialize register whose default after HW reset is problematic or which
+ * are never modified.
+ */
+static int rk3308_codec_initialize(struct rk3308_codec_priv *rk3308)
+{
+ int grp;
+
+ /*
+ * Init ADC digital vol to 0 dB (reset value is 0xff, undocumented).
+ * Range: -97dB ~ +32dB.
+ */
+ if (rk3308->codec_ver == ACODEC_VERSION_C) {
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++) {
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON05(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ regmap_write(rk3308->regmap, RK3308_ADC_DIG_CON06(grp),
+ RK3308_ADC_DIG_VOL_CON_x_0DB);
+ }
+ }
+
+ /* set HPMIX default gains (reset value is 0, which is illegal) */
+ regmap_update_bits(rk3308->regmap, RK3308_DAC_ANA_CON12,
+ RK3308_DAC_L_HPMIX_GAIN_MSK |
+ RK3308_DAC_R_HPMIX_GAIN_MSK,
+ RK3308_DAC_L_HPMIX_GAIN_NDB_6 |
+ RK3308_DAC_R_HPMIX_GAIN_NDB_6);
+
+ /* recover DAC digital gain to 0 dB (reset value is 0xff, undocumented) */
+ if (rk3308->codec_ver == ACODEC_VERSION_C)
+ regmap_write(rk3308->regmap, RK3308_DAC_DIG_CON04,
+ RK3308BS_DAC_DIG_GAIN_0DB);
+
+ /*
+ * Unconditionally enable zero-cross detection (needed for AGC,
+ * harmless without AGC)
+ */
+ for (grp = 0; grp < ADC_LR_GROUP_MAX; grp++)
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON02(grp),
+ RK3308_ADC_CH1_ZEROCROSS_DET_EN |
+ RK3308_ADC_CH2_ZEROCROSS_DET_EN);
+
+ return 0;
+}
+
+static int rk3308_codec_probe(struct snd_soc_component *component)
+{
+ struct rk3308_codec_priv *rk3308 = snd_soc_component_get_drvdata(component);
+
+ rk3308->component = component;
+
+ rk3308_codec_reset(component);
+ rk3308_codec_initialize(rk3308);
+
+ return 0;
+}
+
+static int rk3308_codec_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct rk3308_codec_priv *rk3308 = 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)
+ break;
+
+ /* Sequence from TRM Section 8.6.3 "Power Up" */
+ regmap_set_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_set_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 0x7f);
+ msleep(20); /* estimated value */
+ break;
+ case SND_SOC_BIAS_OFF:
+ /* Sequence from TRM Section 8.6.4 "Power Down" */
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_CURRENT_CHARGE_MSK, 1);
+ regmap_clear_bits(rk3308->regmap, RK3308_ADC_ANA_CON10(0),
+ RK3308_ADC_REF_EN);
+ regmap_clear_bits(rk3308->regmap, RK3308_DAC_ANA_CON02,
+ RK3308_DAC_L_DAC_EN | RK3308_DAC_R_DAC_EN);
+ msleep(20); /* estimated value */
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_component_driver rk3308_codec_component_driver = {
+ .probe = rk3308_codec_probe,
+ .set_bias_level = rk3308_codec_set_bias_level,
+ .controls = rk3308_codec_controls,
+ .num_controls = ARRAY_SIZE(rk3308_codec_controls),
+ .dapm_widgets = rk3308_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk3308_codec_dapm_widgets),
+ .dapm_routes = rk3308_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk3308_codec_dapm_routes),
+};
+
+static const struct regmap_config rk3308_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = RK3308_DAC_ANA_CON15,
+};
+
+static int rk3308_codec_get_version(struct rk3308_codec_priv *rk3308)
+{
+ unsigned int chip_id;
+ int err;
+
+ err = regmap_read(rk3308->grf, GRF_CHIP_ID, &chip_id);
+ if (err)
+ return err;
+
+ switch (chip_id) {
+ case 3306:
+ rk3308->codec_ver = ACODEC_VERSION_A;
+ break;
+ case 0x3308:
+ rk3308->codec_ver = ACODEC_VERSION_B;
+ return dev_err_probe(rk3308->dev, -EINVAL, "Chip version B not supported\n");
+ case 0x3308c:
+ rk3308->codec_ver = ACODEC_VERSION_C;
+ break;
+ default:
+ return dev_err_probe(rk3308->dev, -EINVAL, "Unknown chip_id: 0x%x\n", chip_id);
+ }
+
+ dev_info(rk3308->dev, "Found codec version %c\n", rk3308->codec_ver);
+ return 0;
+}
+
+static int rk3308_codec_set_micbias_level(struct rk3308_codec_priv *rk3308)
+{
+ struct device_node *np = rk3308->dev->of_node;
+ u32 percent;
+ u32 mult;
+ int err;
+
+ err = of_property_read_u32(np, "rockchip,micbias-avdd-percent", &percent);
+ if (err == -EINVAL)
+ return 0;
+ if (err)
+ return dev_err_probe(rk3308->dev, err,
+ "Error reading 'rockchip,micbias-avdd-percent'\n");
+
+ /* Convert percent to register value, linerarly (50% -> 0, 5% step = +1) */
+ mult = (percent - 50) / 5;
+
+ /* Check range and that the percent was an exact value allowed */
+ if (mult > RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX || mult * 5 + 50 != percent)
+ return dev_err_probe(rk3308->dev, -EINVAL,
+ "Invalid value %u for 'rockchip,micbias-avdd-percent'\n",
+ percent);
+
+ regmap_update_bits(rk3308->regmap, RK3308_ADC_ANA_CON07(0),
+ RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK,
+ mult << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT);
+
+ return 0;
+}
+
+static int rk3308_codec_platform_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct rk3308_codec_priv *rk3308;
+ void __iomem *base;
+ int err;
+
+ rk3308 = devm_kzalloc(&pdev->dev, sizeof(*rk3308), GFP_KERNEL);
+ if (!rk3308)
+ return -ENOMEM;
+
+ rk3308->dev = dev;
+
+ rk3308->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+ if (IS_ERR(rk3308->grf))
+ return dev_err_probe(dev, PTR_ERR(rk3308->grf), "Error getting GRF\n");
+
+ rk3308->reset = devm_reset_control_get_optional_exclusive(dev, "codec");
+ if (IS_ERR(rk3308->reset))
+ return dev_err_probe(dev, PTR_ERR(rk3308->reset), "Failed to get reset control\n");
+
+ err = devm_clk_bulk_get(dev, ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to get clocks\n");
+
+ err = clk_bulk_prepare_enable(ARRAY_SIZE(rk3308_codec_clocks), rk3308_codec_clocks);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to enable clocks\n");
+
+ err = rk3308_codec_get_version(rk3308);
+ if (err)
+ return err;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rk3308->regmap = devm_regmap_init_mmio(dev, base, &rk3308_codec_regmap_config);
+ if (IS_ERR(rk3308->regmap))
+ return dev_err_probe(dev, PTR_ERR(rk3308->regmap),
+ "Failed to init regmap\n");
+
+ platform_set_drvdata(pdev, rk3308);
+
+ err = rk3308_codec_set_micbias_level(rk3308);
+ if (err)
+ return err;
+
+ err = devm_snd_soc_register_component(dev, &rk3308_codec_component_driver,
+ &rk3308_codec_dai_driver, 1);
+ if (err)
+ return dev_err_probe(dev, err, "Failed to register codec\n");
+
+ return 0;
+}
+
+static const struct of_device_id __maybe_unused rk3308_codec_of_match[] = {
+ { .compatible = "rockchip,rk3308-codec", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rk3308_codec_of_match);
+
+static struct platform_driver rk3308_codec_driver = {
+ .driver = {
+ .name = "rk3308-acodec",
+ .of_match_table = rk3308_codec_of_match,
+ },
+ .probe = rk3308_codec_platform_probe,
+};
+module_platform_driver(rk3308_codec_driver);
+
+MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
+MODULE_AUTHOR("Luca Ceresoli <luca.ceresoli@bootlin.com>");
+MODULE_DESCRIPTION("ASoC RK3308 Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rk3308_codec.h b/sound/soc/codecs/rk3308_codec.h
new file mode 100644
index 000000000000..a4226b235ab7
--- /dev/null
+++ b/sound/soc/codecs/rk3308_codec.h
@@ -0,0 +1,579 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Rockchip RK3308 internal audio codec driver -- register definitions
+ *
+ * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+ * Copyright (c) 2022, Vivax-Metrotech Ltd
+ */
+
+#ifndef __RK3308_CODEC_H__
+#define __RK3308_CODEC_H__
+
+#define RK3308_GLB_CON 0x00
+
+/* ADC DIGITAL REGISTERS */
+
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_DIG_OFFSET(ch) (((ch) & 0x3) * 0xc0 + 0x0)
+
+#define RK3308_ADC_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x14) // ver.C only
+#define RK3308_ADC_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x18) // ver.C only
+#define RK3308_ADC_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x1c)
+
+#define RK3308_ALC_L_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x40)
+#define RK3308_ALC_L_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x44)
+#define RK3308_ALC_L_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x48)
+#define RK3308_ALC_L_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x4c)
+#define RK3308_ALC_L_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x50)
+#define RK3308_ALC_L_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x54)
+#define RK3308_ALC_L_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x58)
+#define RK3308_ALC_L_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x5c)
+#define RK3308_ALC_L_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x60)
+#define RK3308_ALC_L_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x64)
+#define RK3308_ALC_L_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x70)
+
+#define RK3308_ALC_R_DIG_CON00(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x80)
+#define RK3308_ALC_R_DIG_CON01(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x84)
+#define RK3308_ALC_R_DIG_CON02(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x88)
+#define RK3308_ALC_R_DIG_CON03(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x8c)
+#define RK3308_ALC_R_DIG_CON04(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x90)
+#define RK3308_ALC_R_DIG_CON05(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x94)
+#define RK3308_ALC_R_DIG_CON06(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x98)
+#define RK3308_ALC_R_DIG_CON07(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0x9c)
+#define RK3308_ALC_R_DIG_CON08(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa0)
+#define RK3308_ALC_R_DIG_CON09(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xa4)
+#define RK3308_ALC_R_DIG_CON12(ch) (RK3308_ADC_DIG_OFFSET((ch)) + 0xb0)
+
+/* DAC DIGITAL REGISTERS */
+#define RK3308_DAC_DIG_OFFSET 0x300
+#define RK3308_DAC_DIG_CON01 (RK3308_DAC_DIG_OFFSET + 0x04)
+#define RK3308_DAC_DIG_CON02 (RK3308_DAC_DIG_OFFSET + 0x08)
+#define RK3308_DAC_DIG_CON03 (RK3308_DAC_DIG_OFFSET + 0x0c)
+#define RK3308_DAC_DIG_CON04 (RK3308_DAC_DIG_OFFSET + 0x10)
+#define RK3308_DAC_DIG_CON05 (RK3308_DAC_DIG_OFFSET + 0x14)
+#define RK3308_DAC_DIG_CON10 (RK3308_DAC_DIG_OFFSET + 0x28)
+#define RK3308_DAC_DIG_CON11 (RK3308_DAC_DIG_OFFSET + 0x2c)
+#define RK3308_DAC_DIG_CON13 (RK3308_DAC_DIG_OFFSET + 0x34)
+#define RK3308_DAC_DIG_CON14 (RK3308_DAC_DIG_OFFSET + 0x38)
+
+/* ADC ANALOG REGISTERS */
+/*
+ * The ADC group are 0 ~ 3, that control:
+ *
+ * CH0: left_0(ADC1) and right_0(ADC2)
+ * CH1: left_1(ADC3) and right_1(ADC4)
+ * CH2: left_2(ADC5) and right_2(ADC6)
+ * CH3: left_3(ADC7) and right_3(ADC8)
+ */
+#define RK3308_ADC_ANA_OFFSET(ch) (((ch) & 0x3) * 0x40 + 0x340)
+#define RK3308_ADC_ANA_CON00(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x00)
+#define RK3308_ADC_ANA_CON01(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x04)
+#define RK3308_ADC_ANA_CON02(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x08)
+#define RK3308_ADC_ANA_CON03(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x0c)
+#define RK3308_ADC_ANA_CON04(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x10)
+#define RK3308_ADC_ANA_CON05(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x14)
+#define RK3308_ADC_ANA_CON06(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x18)
+#define RK3308_ADC_ANA_CON07(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x1c)
+#define RK3308_ADC_ANA_CON08(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x20)
+#define RK3308_ADC_ANA_CON10(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x28)
+#define RK3308_ADC_ANA_CON11(ch) (RK3308_ADC_ANA_OFFSET((ch)) + 0x2c)
+
+/* DAC ANALOG REGISTERS */
+#define RK3308_DAC_ANA_OFFSET 0x440
+#define RK3308_DAC_ANA_CON00 (RK3308_DAC_ANA_OFFSET + 0x00)
+#define RK3308_DAC_ANA_CON01 (RK3308_DAC_ANA_OFFSET + 0x04)
+#define RK3308_DAC_ANA_CON02 (RK3308_DAC_ANA_OFFSET + 0x08)
+#define RK3308_DAC_ANA_CON03 (RK3308_DAC_ANA_OFFSET + 0x0c)
+#define RK3308_DAC_ANA_CON04 (RK3308_DAC_ANA_OFFSET + 0x10)
+#define RK3308_DAC_ANA_CON05 (RK3308_DAC_ANA_OFFSET + 0x14)
+#define RK3308_DAC_ANA_CON06 (RK3308_DAC_ANA_OFFSET + 0x18)
+#define RK3308_DAC_ANA_CON07 (RK3308_DAC_ANA_OFFSET + 0x1c)
+#define RK3308_DAC_ANA_CON08 (RK3308_DAC_ANA_OFFSET + 0x20)
+#define RK3308_DAC_ANA_CON12 (RK3308_DAC_ANA_OFFSET + 0x30)
+#define RK3308_DAC_ANA_CON13 (RK3308_DAC_ANA_OFFSET + 0x34)
+#define RK3308_DAC_ANA_CON14 (RK3308_DAC_ANA_OFFSET + 0x38)
+#define RK3308_DAC_ANA_CON15 (RK3308_DAC_ANA_OFFSET + 0x3c)
+
+/*
+ * These are the bits for registers
+ */
+
+/* RK3308_GLB_CON - REG: 0x0000 */
+#define RK3308_ADC_BIST_WORK BIT(7)
+#define RK3308_DAC_BIST_WORK BIT(6)
+#define RK3308_ADC_MCLK_GATING BIT(5)
+#define RK3308_DAC_MCLK_GATING BIT(4)
+#define RK3308_ADC_DIG_WORK BIT(2)
+#define RK3308_DAC_DIG_WORK BIT(1)
+#define RK3308_SYS_WORK BIT(0)
+
+/* RK3308_ADC_DIG_CON01 - REG: 0x0004 */
+#define RK3308_ADC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_ADC_I2S_VALID_LEN_SFT 5
+#define RK3308_ADC_I2S_VALID_LEN_MSK (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_32BITS (0x3 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_24BITS (0x2 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_20BITS (0x1 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_VALID_LEN_16BITS (0x0 << RK3308_ADC_I2S_VALID_LEN_SFT)
+#define RK3308_ADC_I2S_MODE_SFT 3
+#define RK3308_ADC_I2S_MODE_MSK (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_PCM (0x3 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_I2S (0x2 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_LJ (0x1 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_MODE_RJ (0x0 << RK3308_ADC_I2S_MODE_SFT)
+#define RK3308_ADC_I2S_LR_SWAP BIT(1)
+#define RK3308_ADC_I2S_MONO BIT(0)
+
+/* RK3308_ADC_DIG_CON02 - REG: 0x0008 */
+#define RK3308_ADC_IO_MODE_MASTER BIT(5)
+#define RK3308_ADC_MODE_MASTER BIT(4)
+#define RK3308_ADC_I2S_FRAME_LEN_SFT 2
+#define RK3308_ADC_I2S_FRAME_LEN_MSK (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_32BITS (0x3 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_24BITS (0x2 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_20BITS (0x1 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_FRAME_16BITS (0x0 << RK3308_ADC_I2S_FRAME_LEN_SFT)
+#define RK3308_ADC_I2S_WORK BIT(1)
+#define RK3308_ADC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_ADC_DIG_CON03 - REG: 0x000c */
+#define RK3308_ADC_L_CH_BIST_SFT 2
+#define RK3308_ADC_L_CH_BIST_MSK (0x3 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_RIGHT (0x3 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_L_CH_BIST_CUBE (0x2 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_BIST_SINE (0x1 << RK3308_ADC_L_CH_BIST_SFT)
+#define RK3308_ADC_L_CH_NORMAL_LEFT (0x0 << RK3308_ADC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_SFT 0
+#define RK3308_ADC_R_CH_BIST_MSK (0x3 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_LEFT (0x3 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_ADC_R_CH_BIST_CUBE (0x2 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_BIST_SINE (0x1 << RK3308_ADC_R_CH_BIST_SFT)
+#define RK3308_ADC_R_CH_NORMAL_RIGHT (0x0 << RK3308_ADC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_ADC_DIG_CON04 - REG: 0x0010 */
+#define RK3308_ADC_HPF_PATH_DIS BIT(2)
+#define RK3308_ADC_HPF_CUTOFF_SFT 0
+#define RK3308_ADC_HPF_CUTOFF_MSK (0x3 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_612HZ (0x2 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_245HZ (0x1 << RK3308_ADC_HPF_CUTOFF_SFT)
+#define RK3308_ADC_HPF_CUTOFF_20HZ (0x0 << RK3308_ADC_HPF_CUTOFF_SFT)
+
+/* RK3308_ADC_DIG_CON07 - REG: 0x001c */
+#define RK3308_ADCL_DATA_SFT 4
+#define RK3308_ADCR_DATA_SFT 2
+#define RK3308_ADCL_DATA_SEL_ADCL BIT(1)
+#define RK3308_ADCR_DATA_SEL_ADCR BIT(0)
+
+/*
+ * RK3308_ALC_L_DIG_CON00 - REG: 0x0040 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON00 - REG: 0x0080 + ch * 0xc0
+ */
+#define RK3308_GAIN_ATTACK_JACK BIT(6)
+#define RK3308_CTRL_GEN_SFT 4
+#define RK3308_CTRL_GEN_MSK (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK3 (0x3 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK2 (0x2 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_JACK1 (0x1 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_CTRL_GEN_NORMAL (0x0 << RK3308_ALC_CTRL_GEN_SFT)
+#define RK3308_AGC_HOLD_TIME_SFT 0
+#define RK3308_AGC_HOLD_TIME_MSK (0xf << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_1S (0xa << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_512MS (0x9 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_256MS (0x8 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_128MS (0x7 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_64MS (0x6 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_32MS (0x5 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_16MS (0x4 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_8MS (0x3 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_4MS (0x2 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_2MS (0x1 << RK3308_AGC_HOLD_TIME_SFT)
+#define RK3308_AGC_HOLD_TIME_0MS (0x0 << RK3308_AGC_HOLD_TIME_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON01 - REG: 0x0044 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON01 - REG: 0x0084 + ch * 0xc0
+ */
+#define RK3308_AGC_DECAY_TIME_SFT 4
+#define RK3308_AGC_ATTACK_TIME_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON02 - REG: 0x0048 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON02 - REG: 0x0088 + ch * 0xc0
+ */
+#define RK3308_AGC_MODE_LIMITER BIT(7)
+#define RK3308_AGC_ZERO_CRO_EN BIT(6)
+#define RK3308_AGC_AMP_RECOVER_GAIN BIT(5)
+#define RK3308_AGC_FAST_DEC_EN BIT(4)
+#define RK3308_AGC_NOISE_GATE_EN BIT(3)
+#define RK3308_AGC_NOISE_GATE_THRESH_SFT 0
+#define RK3308_AGC_NOISE_GATE_THRESH_MSK (0x7 << RK3308_AGC_NOISE_GATE_THRESH_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON03 - REG: 0x004c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON03 - REG: 0x008c + ch * 0xc0
+ */
+#define RK3308_AGC_PGA_ZERO_CRO_EN BIT(5)
+#define RK3308_AGC_PGA_GAIN_MAX 0x1f
+#define RK3308_AGC_PGA_GAIN_MIN 0
+#define RK3308_AGC_PGA_GAIN_SFT 0
+
+/*
+ * RK3308_ALC_L_DIG_CON04 - REG: 0x0050 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON04 - REG: 0x0090 + ch * 0xc0
+ */
+#define RK3308_AGC_SLOW_CLK_EN BIT(3)
+#define RK3308_AGC_APPROX_RATE_SFT 0
+#define RK3308_AGC_APPROX_RATE_MSK (0x7 << RK3308_AGC_APPROX_RATE_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON05 - REG: 0x0054 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON05 - REG: 0x0094 + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON06 - REG: 0x0058 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON06 - REG: 0x0098 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MAX_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON07 - REG: 0x005c + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON07 - REG: 0x009c + ch * 0xc0
+ */
+#define RK3308_AGC_LO_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON08 - REG: 0x0060 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON08 - REG: 0x00a0 + ch * 0xc0
+ */
+#define RK3308_AGC_HI_8BITS_AGC_MIN_MSK 0xff
+
+/*
+ * RK3308_ALC_L_DIG_CON09 - REG: 0x0064 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON09 - REG: 0x00a4 + ch * 0xc0
+ */
+#define RK3308_AGC_FUNC_SEL BIT(6)
+#define RK3308_AGC_MAX_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MAX_GAIN_PGA_MIN 0
+#define RK3308_AGC_MAX_GAIN_PGA_SFT 3
+#define RK3308_AGC_MAX_GAIN_PGA_MSK (0x7 << RK3308_AGC_MAX_GAIN_PGA_SFT)
+#define RK3308_AGC_MIN_GAIN_PGA_MAX 0x7
+#define RK3308_AGC_MIN_GAIN_PGA_MIN 0
+#define RK3308_AGC_MIN_GAIN_PGA_SFT 0
+#define RK3308_AGC_MIN_GAIN_PGA_MSK (0x7 << RK3308_AGC_MIN_GAIN_PGA_SFT)
+
+/*
+ * RK3308_ALC_L_DIG_CON12 - REG: 0x0068 + ch * 0xc0
+ * RK3308_ALC_R_DIG_CON12 - REG: 0x00a8 + ch * 0xc0
+ */
+#define RK3308_AGC_GAIN_MSK 0x1f
+
+/* RK3308_DAC_DIG_CON01 - REG: 0x0304 */
+#define RK3308_DAC_I2S_LRC_POL_REVERSAL BIT(7)
+#define RK3308_DAC_I2S_VALID_LEN_SFT 5
+#define RK3308_DAC_I2S_VALID_LEN_MSK (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_32BITS (0x3 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_24BITS (0x2 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_20BITS (0x1 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_VALID_LEN_16BITS (0x0 << RK3308_DAC_I2S_VALID_LEN_SFT)
+#define RK3308_DAC_I2S_MODE_SFT 3
+#define RK3308_DAC_I2S_MODE_MSK (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_PCM (0x3 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_I2S (0x2 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_LJ (0x1 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_MODE_RJ (0x0 << RK3308_DAC_I2S_MODE_SFT)
+#define RK3308_DAC_I2S_LR_SWAP BIT(2)
+
+/* RK3308_DAC_DIG_CON02 - REG: 0x0308 */
+#define RK3308BS_DAC_IO_MODE_MASTER BIT(7)
+#define RK3308BS_DAC_MODE_MASTER BIT(6)
+#define RK3308_DAC_IO_MODE_MASTER BIT(5)
+#define RK3308_DAC_MODE_MASTER BIT(4)
+#define RK3308_DAC_I2S_FRAME_LEN_SFT 2
+#define RK3308_DAC_I2S_FRAME_LEN_MSK (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_32BITS (0x3 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_24BITS (0x2 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_20BITS (0x1 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_FRAME_16BITS (0x0 << RK3308_DAC_I2S_FRAME_LEN_SFT)
+#define RK3308_DAC_I2S_WORK BIT(1)
+#define RK3308_DAC_I2S_BIT_CLK_POL_REVERSAL BIT(0)
+
+/* RK3308_DAC_DIG_CON03 - REG: 0x030C */
+#define RK3308_DAC_L_CH_BIST_SFT 2
+#define RK3308_DAC_L_CH_BIST_MSK (0x3 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_LEFT (0x3 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_L_CH_BIST_CUBE (0x2 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_SINE (0x1 << RK3308_DAC_L_CH_BIST_SFT)
+#define RK3308_DAC_L_CH_BIST_RIGHT (0x0 << RK3308_DAC_L_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_SFT 0
+#define RK3308_DAC_R_CH_BIST_MSK (0x3 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_LEFT (0x3 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+#define RK3308_DAC_R_CH_BIST_CUBE (0x2 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_SINE (0x1 << RK3308_DAC_R_CH_BIST_SFT)
+#define RK3308_DAC_R_CH_BIST_RIGHT (0x0 << RK3308_DAC_R_CH_BIST_SFT) /* normal mode */
+
+/* RK3308_DAC_DIG_CON04 - REG: 0x0310 */
+/* Versions up to B: */
+#define RK3308_DAC_MODULATOR_GAIN_SFT 4
+#define RK3308_DAC_MODULATOR_GAIN_MSK (0x7 << RK3308_DAC_MODULATOR_GAIN_SFT)
+#define RK3308_DAC_CIC_IF_GAIN_SFT 0
+#define RK3308_DAC_CIC_IF_GAIN_MSK (0x7 << RK3308_DAC_CIC_IF_GAIN_SFT)
+/* Version C: */
+#define RK3308BS_DAC_DIG_GAIN_SFT 0
+#define RK3308BS_DAC_DIG_GAIN_MSK (0xff << RK3308BS_DAC_DIG_GAIN_SFT)
+#define RK3308BS_DAC_DIG_GAIN_0DB (0xed << RK3308BS_DAC_DIG_GAIN_SFT)
+
+/* RK3308BS_ADC_DIG_CON05..06 (Version C only) */
+#define RK3308_ADC_DIG_VOL_CON_x_SFT 0
+#define RK3308_ADC_DIG_VOL_CON_x_MSK (0xff << RK3308_ADC_DIG_VOL_CON_x_SFT)
+#define RK3308_ADC_DIG_VOL_CON_x_0DB (0xc2 << RK3308_ADC_DIG_VOL_CON_x_SFT)
+
+/* RK3308_DAC_DIG_CON05 - REG: 0x0314 */
+#define RK3308_DAC_L_REG_CTL_INDATA BIT(2)
+#define RK3308_DAC_R_REG_CTL_INDATA BIT(1)
+
+/* RK3308_DAC_DIG_CON10 - REG: 0x0328 */
+#define RK3308_DAC_DATA_HI4(x) ((x) & 0xf)
+
+/* RK3308_DAC_DIG_CON11 - REG: 0x032c */
+#define RK3308_DAC_DATA_LO8(x) ((x) & 0xff)
+
+/* RK3308_ADC_ANA_CON00 - REG: 0x0340 */
+#define RK3308_ADC_CH1_CH2_MIC_ALL_MSK (0xff << 0)
+#define RK3308_ADC_CH1_CH2_MIC_ALL 0xff
+#define RK3308_ADC_CH2_MIC_UNMUTE BIT(7)
+#define RK3308_ADC_CH2_MIC_WORK BIT(6)
+#define RK3308_ADC_CH2_MIC_EN BIT(5)
+#define RK3308_ADC_CH2_BUF_REF_EN BIT(4)
+#define RK3308_ADC_CH1_MIC_UNMUTE BIT(3)
+#define RK3308_ADC_CH1_MIC_WORK BIT(2)
+#define RK3308_ADC_CH1_MIC_EN BIT(1)
+#define RK3308_ADC_CH1_BUF_REF_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON01 - REG: 0x0344
+ *
+ * The PGA of MIC-INs:
+ * - HW version A:
+ * 0x0 - MIC1~MIC8 0 dB (recommended when ADC used as loopback)
+ * 0x3 - MIC1~MIC8 20 dB (recommended when ADC used as MIC input)
+ * - HW version B:
+ * 0x0 - MIC1~MIC8 0 dB
+ * 0x1 - MIC1~MIC8 6.6 dB
+ * 0x2 - MIC1~MIC8 13 dB
+ * 0x3 - MIC1~MIC8 20 dB
+ */
+#define RK3308_ADC_CH2_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH2_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH2_MIC_GAIN_SFT 4
+#define RK3308_ADC_CH2_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+#define RK3308_ADC_CH2_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH2_MIC_GAIN_SFT)
+
+#define RK3308_ADC_CH1_MIC_GAIN_MAX 0x3
+#define RK3308_ADC_CH1_MIC_GAIN_MIN 0
+#define RK3308_ADC_CH1_MIC_GAIN_SFT 0
+#define RK3308_ADC_CH1_MIC_GAIN_MSK (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_20DB (0x3 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_13DB (0x2 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_6_6DB (0x1 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+#define RK3308_ADC_CH1_MIC_GAIN_0DB (0x0 << RK3308_ADC_CH1_MIC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON02 - REG: 0x0348 */
+#define RK3308_ADC_CH2_ZEROCROSS_DET_EN BIT(6)
+#define RK3308_ADC_CH2_ALC_WORK BIT(5)
+#define RK3308_ADC_CH2_ALC_EN BIT(4)
+#define RK3308_ADC_CH1_ZEROCROSS_DET_EN BIT(2)
+#define RK3308_ADC_CH1_ALC_WORK BIT(1)
+#define RK3308_ADC_CH1_ALC_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON03 - REG: 0x034c */
+#define RK3308_ADC_CH1_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH1_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH1_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH1_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH1_ALC_GAIN_SFT)
+#define RK3308_ADC_CH1_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH1_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON04 - REG: 0x0350 */
+#define RK3308_ADC_CH2_ALC_GAIN_MAX 0x1f
+#define RK3308_ADC_CH2_ALC_GAIN_MIN 0
+#define RK3308_ADC_CH2_ALC_GAIN_SFT 0
+#define RK3308_ADC_CH2_ALC_GAIN_MSK (0x1f << RK3308_ADC_CH2_ALC_GAIN_SFT)
+#define RK3308_ADC_CH2_ALC_GAIN_0DB (0x0c << RK3308_ADC_CH2_ALC_GAIN_SFT)
+
+/* RK3308_ADC_ANA_CON05 - REG: 0x0354 */
+#define RK3308_ADC_CH2_ADC_WORK BIT(6)
+#define RK3308_ADC_CH2_ADC_EN BIT(5)
+#define RK3308_ADC_CH2_CLK_EN BIT(4)
+#define RK3308_ADC_CH1_ADC_WORK BIT(2)
+#define RK3308_ADC_CH1_ADC_EN BIT(1)
+#define RK3308_ADC_CH1_CLK_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON06 - REG: 0x0358 */
+#define RK3308_ADC_CURRENT_EN BIT(0)
+
+/* RK3308_ADC_ANA_CON07 - REG: 0x035c */
+/* Note: The register configuration is only valid for ADC2 */
+#define RK3308_ADC_CH2_IN_SEL_SFT 6
+#define RK3308_ADC_CH2_IN_SEL_MSK (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_LINEIN (0x2 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_MIC (0x1 << RK3308_ADC_CH2_IN_SEL_SFT)
+#define RK3308_ADC_CH2_IN_NONE (0x0 << RK3308_ADC_CH2_IN_SEL_SFT)
+/* Note: The register configuration is only valid for ADC1 */
+#define RK3308_ADC_CH1_IN_SEL_SFT 4
+#define RK3308_ADC_CH1_IN_SEL_MSK (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN_MIC (0x3 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_LINEIN (0x2 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_MIC (0x1 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_CH1_IN_NONE (0x0 << RK3308_ADC_CH1_IN_SEL_SFT)
+#define RK3308_ADC_MIC_BIAS_BUF_EN BIT(3)
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MAX 7
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT 0
+#define RK3308_ADC_LEVEL_RANGE_MICBIAS_MSK (0x7 << RK3308_ADC_LEVEL_RANGE_MICBIAS_SFT)
+
+/* RK3308_ADC_ANA_CON08 - REG: 0x0360 */
+#define RK3308_ADC_MICBIAS_CURRENT_EN BIT(4)
+
+/* RK3308_ADC_ANA_CON10 - REG: 0x0368 */
+#define RK3308_ADC_REF_EN BIT(7)
+#define RK3308_ADC_CURRENT_CHARGE_SFT 0
+#define RK3308_ADC_CURRENT_CHARGE_MSK (0x7f << RK3308_ADC_CURRENT_CHARGE_SFT)
+
+/* RK3308_ADC_ANA_CON11 - REG: 0x036c */
+#define RK3308_ADC_ALCR_CON_GAIN_PGAR_EN BIT(1)
+#define RK3308_ADC_ALCL_CON_GAIN_PGAL_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON00 - REG: 0x0440 */
+#define RK3308_DAC_HEADPHONE_DET_EN BIT(1)
+#define RK3308_DAC_CURRENT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON01 - REG: 0x0444 */
+#define RK3308_DAC_BUF_REF_R_EN BIT(6)
+#define RK3308_DAC_BUF_REF_L_EN BIT(2)
+#define RK3308_DAC_HPOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_HPOUT_POP_SOUND_L_SFT 0
+// unshifted values for both L and R:
+#define RK3308_DAC_HPOUT_POP_SOUND_x_MSK 0x3
+#define RK3308_DAC_HPOUT_POP_SOUND_x_WORK 0x2
+#define RK3308_DAC_HPOUT_POP_SOUND_x_INIT 0x1
+
+/* RK3308_DAC_ANA_CON02 - REG: 0x0448 */
+#define RK3308_DAC_R_DAC_WORK BIT(7)
+#define RK3308_DAC_R_DAC_EN BIT(6)
+#define RK3308_DAC_R_CLK_EN BIT(5)
+#define RK3308_DAC_R_REF_EN BIT(4)
+#define RK3308_DAC_L_DAC_WORK BIT(3)
+#define RK3308_DAC_L_DAC_EN BIT(2)
+#define RK3308_DAC_L_CLK_EN BIT(1)
+#define RK3308_DAC_L_REF_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON03 - REG: 0x044c */
+#define RK3308_DAC_R_HPOUT_WORK BIT(6)
+#define RK3308_DAC_R_HPOUT_EN BIT(5)
+#define RK3308_DAC_R_HPOUT_MUTE_SFT 4
+#define RK3308_DAC_L_HPOUT_WORK BIT(2)
+#define RK3308_DAC_L_HPOUT_EN BIT(1)
+#define RK3308_DAC_L_HPOUT_MUTE_SFT 0
+
+/* RK3308_DAC_ANA_CON04 - REG: 0x0450 */
+#define RK3308_DAC_x_LINEOUT_GAIN_MAX 0x3
+#define RK3308_DAC_R_LINEOUT_GAIN_SFT 6
+#define RK3308_DAC_R_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_R_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_R_LINEOUT_MUTE_SFT 5
+#define RK3308_DAC_R_LINEOUT_EN BIT(4)
+#define RK3308_DAC_L_LINEOUT_GAIN_SFT 2
+#define RK3308_DAC_L_LINEOUT_GAIN_MSK (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_0DB (0x3 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_1_5 (0x2 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_3 (0x1 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_GAIN_NDB_6 (0x0 << RK3308_DAC_L_LINEOUT_GAIN_SFT)
+#define RK3308_DAC_L_LINEOUT_MUTE_SFT 1
+#define RK3308_DAC_L_LINEOUT_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON05 - REG: 0x0454, step is 1.5db */
+/* RK3308_DAC_ANA_CON06 - REG: 0x0458, step is 1.5db */
+#define RK3308_DAC_x_HPOUT_GAIN_MAX 0x1e
+#define RK3308_DAC_x_HPOUT_GAIN_SFT 0
+#define RK3308_DAC_x_HPOUT_GAIN_MSK (0x1f << RK3308_DAC_x_HPOUT_GAIN_SFT)
+#define RK3308_DAC_x_HPOUT_GAIN_MIN (0x00 << RK3308_DAC_x_HPOUT_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON07 - REG: 0x045c */
+#define RK3308_DAC_R_HPOUT_DRV_SFT 4
+#define RK3308_DAC_R_HPOUT_DRV_MSK (0xf << RK3308_DAC_R_HPOUT_DRV_SFT)
+#define RK3308_DAC_L_HPOUT_DRV_SFT 0
+#define RK3308_DAC_L_HPOUT_DRV_MSK (0xf << RK3308_DAC_L_HPOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON08 - REG: 0x0460 */
+#define RK3308_DAC_R_LINEOUT_DRV_SFT 4
+#define RK3308_DAC_R_LINEOUT_DRV_MSK (0xf << RK3308_DAC_R_LINEOUT_DRV_SFT)
+#define RK3308_DAC_L_LINEOUT_DRV_SFT 0
+#define RK3308_DAC_L_LINEOUT_DRV_MSK (0xf << RK3308_DAC_L_LINEOUT_DRV_SFT)
+
+/* RK3308_DAC_ANA_CON12 - REG: 0x0470 */
+#define RK3308_DAC_R_HPMIX_SEL_SFT 6
+#define RK3308_DAC_R_HPMIX_SEL_MSK (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_LINEIN (0x2 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_I2S (0x1 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_R_HPMIX_NONE (0x0 << RK3308_DAC_R_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_SEL_SFT 2
+#define RK3308_DAC_L_HPMIX_SEL_MSK (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN_I2S (0x3 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_LINEIN (0x2 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_I2S (0x1 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_L_HPMIX_NONE (0x0 << RK3308_DAC_L_HPMIX_SEL_SFT)
+#define RK3308_DAC_x_HPMIX_GAIN_MIN 0x1 /* 0x0 and 0x3 are reserved */
+#define RK3308_DAC_x_HPMIX_GAIN_MAX 0x2
+#define RK3308_DAC_R_HPMIX_GAIN_SFT 4
+#define RK3308_DAC_R_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_R_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_R_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_SFT 0
+#define RK3308_DAC_L_HPMIX_GAIN_MSK (0x3 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_0DB (0x2 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+#define RK3308_DAC_L_HPMIX_GAIN_NDB_6 (0x1 << RK3308_DAC_L_HPMIX_GAIN_SFT)
+
+/* RK3308_DAC_ANA_CON13 - REG: 0x0474 */
+#define RK3308_DAC_R_HPMIX_UNMUTE BIT(6)
+#define RK3308_DAC_R_HPMIX_WORK BIT(5)
+#define RK3308_DAC_R_HPMIX_EN BIT(4)
+#define RK3308_DAC_L_HPMIX_UNMUTE BIT(2)
+#define RK3308_DAC_L_HPMIX_WORK BIT(1)
+#define RK3308_DAC_L_HPMIX_EN BIT(0)
+
+/* RK3308_DAC_ANA_CON14 - REG: 0x0478 */
+#define RK3308_DAC_VCM_LINEOUT_EN (0x1 << 4)
+#define RK3308_DAC_CURRENT_CHARGE_SFT 0
+#define RK3308_DAC_CURRENT_CHARGE_MSK (0xf << RK3308_DAC_CURRENT_CHARGE_SFT)
+
+/* RK3308_DAC_ANA_CON15 - REG: 0x047C */
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_SFT 4
+#define RK3308_DAC_LINEOUT_POP_SOUND_R_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_R_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_R_SFT)
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_SFT 0
+#define RK3308_DAC_LINEOUT_POP_SOUND_L_MSK (0x3 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_INTERNAL (0x2 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_DC_FROM_VCM (0x1 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+#define RK3308_DAC_L_SEL_LINEOUT_FROM_INTERNAL (0x0 << RK3308_DAC_LINEOUT_POP_SOUND_L_SFT)
+
+#endif /* __RK3308_CODEC_H__ */
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
new file mode 100644
index 000000000000..9697aefc6e03
--- /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 <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#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 <sugar.zhang@rock-chips.com>");
+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 000000000000..655103586241
--- /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 <linux/bitfield.h>
+
+/* 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 000000000000..3c9957b00881
--- /dev/null
+++ b/sound/soc/codecs/rk817_codec.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rk817 ALSA SoC Audio driver
+//
+// Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mfd/rk808.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+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_CBC_CFC:
+ i2s_mst |= RK817_I2S_MODE_SLV;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ 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 = rk817_platform_remove,
+};
+
+module_platform_driver(rk817_codec_driver);
+
+MODULE_DESCRIPTION("ASoC RK817 codec driver");
+MODULE_AUTHOR("binyuan <kevan.lan@rock-chips.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:rk817-codec");
diff --git a/sound/soc/codecs/rl6231.c b/sound/soc/codecs/rl6231.c
index 56650d6c2f53..d1fc1706422f 100644
--- a/sound/soc/codecs/rl6231.c
+++ b/sound/soc/codecs/rl6231.c
@@ -1,53 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rl6231.c - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/gcd.h>
#include "rl6231.h"
/**
- * rl6231_calc_dmic_clk - Calculate the parameter of dmic.
+ * 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 dmic clock between 1MHz and 3MHz.
- * It is better for clock to approximate 3MHz.
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
*/
int rl6231_calc_dmic_clk(int rate)
{
- int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
- int i, red, bound, temp;
+ 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;
+ }
- red = 3000000 * 12;
for (i = 0; i < ARRAY_SIZE(div); i++) {
- bound = div[i] * 3000000;
- if (rate > bound)
+ if ((div[i] % 3) == 0)
continue;
- temp = bound - rate;
- if (temp < red) {
- red = temp;
- idx = i;
- }
+ /* find divider that gives DMIC frequency below 1.536MHz */
+ if (1536000 * div[i] >= rate)
+ return i;
}
- return idx;
+ 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 and bypass flag.
+ * @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.
*
@@ -57,54 +141,89 @@ 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 k, red, n_t, pll_out, in_t, out_t;
- int n = 0, m = 0, m_t = 0;
- int red_t = abs(freq_out - freq_in);
- bool bypass = false;
+ 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;
- k = 100000000 / freq_out - 2;
- if (k > RL6231_PLL_K_MAX)
- k = RL6231_PLL_K_MAX;
- for (n_t = 0; n_t <= max_n; n_t++) {
- in_t = freq_in / (k + 2);
- pll_out = freq_out / (n_t + 2);
- if (in_t < 0)
- continue;
- if (in_t == pll_out) {
- bypass = true;
- n = n_t;
+ 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;
}
- red = abs(in_t - pll_out);
- if (red < red_t) {
- bypass = true;
- n = n_t;
- m = m_t;
- if (red == 0)
+ }
+
+ 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;
- red_t = red;
- }
- for (m_t = 0; m_t <= max_m; m_t++) {
- out_t = in_t / (m_t + 2);
- red = abs(out_t - pll_out);
+ }
+ out_t = in_t / (k_t + 2);
+ red = abs(f_out - out_t);
if (red < red_t) {
- bypass = false;
+ m_bypass = true;
n = n_t;
- m = m_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 = bypass;
+ 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;
@@ -114,7 +233,8 @@ EXPORT_SYMBOL_GPL(rl6231_pll_calc);
int rl6231_get_clk_info(int sclk, int rate)
{
- int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
+ int i;
+ static const int pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
if (sclk <= 0 || rate <= 0)
return -EINVAL;
diff --git a/sound/soc/codecs/rl6231.h b/sound/soc/codecs/rl6231.h
index 0f7b057ed736..928082750860 100644
--- a/sound/soc/codecs/rl6231.h
+++ b/sound/soc/codecs/rl6231.h
@@ -1,19 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rl6231.h - RL6231 class device shared support
*
* Copyright 2014 Realtek Semiconductor Corp.
*
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * 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 __RL6231_H__
#define __RL6231_H__
-#define RL6231_PLL_INP_MAX 40000000
+#define RL6231_PLL_INP_MAX 50000000
#define RL6231_PLL_INP_MIN 256000
#define RL6231_PLL_N_MAX 0x1ff
#define RL6231_PLL_K_MAX 0x1f
@@ -21,6 +18,7 @@
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;
@@ -30,5 +28,6 @@ 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 000000000000..fa8ac34549eb
--- /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 <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#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 <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rl6347a.h b/sound/soc/codecs/rl6347a.h
new file mode 100644
index 000000000000..761455a2fa38
--- /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 <oder_chiou@realtek.com>
+ */
+#ifndef __RL6347A_H__
+#define __RL6347A_H__
+
+#include <sound/hda_verbs.h>
+
+#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/rt-sdw-common.c b/sound/soc/codecs/rt-sdw-common.c
new file mode 100644
index 000000000000..ad61943ce75f
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.c
@@ -0,0 +1,238 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt-sdw-common.c
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/jack.h>
+
+#include "rt-sdw-common.h"
+
+/**
+ * rt_sdca_index_write - Write a value to Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_write(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to set value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_write);
+
+/**
+ * rt_sdca_index_read - Read value from Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @value: value.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value)
+{
+ unsigned int addr = (nid << 20) | reg;
+ int ret;
+
+ ret = regmap_read(map, addr, value);
+ if (ret < 0)
+ pr_err("Failed to get value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_read);
+
+/**
+ * rt_sdca_index_update_bits - Update value on Realtek defined register.
+ *
+ * @map: map for setting.
+ * @nid: Realtek-defined ID.
+ * @reg: register.
+ * @mask: Bitmask to change
+ * @val: New value for bitmask
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt_sdca_index_read(map, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt_sdca_index_write(map, nid, reg, tmp);
+}
+EXPORT_SYMBOL_GPL(rt_sdca_index_update_bits);
+
+/**
+ * rt_sdca_btn_type - Decision of button type.
+ *
+ * @buffer: UMP message buffer.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_btn_type(unsigned char *buffer)
+{
+ u8 btn_type = 0;
+ int ret = 0;
+
+ btn_type |= buffer[0] & 0xf;
+ btn_type |= (buffer[0] >> 4) & 0xf;
+ btn_type |= buffer[1] & 0xf;
+ btn_type |= (buffer[1] >> 4) & 0xf;
+
+ if (btn_type & BIT(0))
+ ret |= SND_JACK_BTN_2;
+ if (btn_type & BIT(1))
+ ret |= SND_JACK_BTN_3;
+ if (btn_type & BIT(2))
+ ret |= SND_JACK_BTN_0;
+ if (btn_type & BIT(3))
+ ret |= SND_JACK_BTN_1;
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_btn_type);
+
+/**
+ * rt_sdca_headset_detect - Headset jack type detection.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ *
+ * A headset jack type will be returned, a negative errno will
+ * be returned in error cases.
+ */
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id)
+{
+ unsigned int det_mode, jack_type;
+ int ret;
+
+ /* get detected_mode */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+
+ if (ret < 0)
+ goto io_error;
+
+ switch (det_mode) {
+ case 0x03:
+ jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ jack_type = SND_JACK_HEADSET;
+ break;
+ default:
+ jack_type = 0;
+ break;
+ }
+
+ /* write selected_mode */
+ if (det_mode) {
+ ret = regmap_write(map, SDW_SDCA_CTL(SDCA_NUM_JACK_CODEC, entity_id,
+ RT_SDCA_CTL_SELECTED_MODE, 0), det_mode);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ return jack_type;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_headset_detect);
+
+/**
+ * rt_sdca_button_detect - Read UMP message and decide button type.
+ *
+ * @map: map for setting.
+ * @entity_id: SDCA entity ID.
+ * @hid_buf_addr: HID buffer address.
+ * @hid_id: Report ID for HID.
+ *
+ * A button type will be returned regarding to buffer,
+ * it returns zero if buffer cannot be recognized.
+ */
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ unsigned char buf[3];
+ int ret;
+
+ /* get current UMP message owner */
+ ret = regmap_read(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_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(map, SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto _end_btn_det_;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(map, hid_buf_addr + offset + idx, &val);
+ if (ret < 0)
+ goto _end_btn_det_;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID */
+ if (buf[0] == hid_id)
+ btn_type = rt_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(map,
+ SDW_SDCA_CTL(SDCA_NUM_HID, entity_id,
+ RT_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+EXPORT_SYMBOL_GPL(rt_sdca_button_detect);
+
+MODULE_DESCRIPTION("Realtek soundwire common functions");
+MODULE_AUTHOR("jack yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt-sdw-common.h b/sound/soc/codecs/rt-sdw-common.h
new file mode 100644
index 000000000000..4759516feb38
--- /dev/null
+++ b/sound/soc/codecs/rt-sdw-common.h
@@ -0,0 +1,66 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+//
+// rt-sdw-common.h
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+
+/*
+ * This file defines common functions used with Realtek soundwire codecs.
+ */
+
+#ifndef __RT_SDW_COMMON_H__
+#define __RT_SDW_COMMON_H__
+
+#define SDCA_NUM_JACK_CODEC 0x01
+#define SDCA_NUM_MIC_ARRAY 0x02
+#define SDCA_NUM_HID 0x03
+#define SDCA_NUM_AMP 0x04
+#define RT_SDCA_CTL_SELECTED_MODE 0x01
+#define RT_SDCA_CTL_DETECTED_MODE 0x02
+#define RT_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+
+struct rt_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+#define RT_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount, \
+ xinfo, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = xinfo, \
+ .get = xget, \
+ .put = xput, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\
+ xhandler_put, xcount, xmax, tlv_array, xinfo) \
+{ .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 = xinfo, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+
+int rt_sdca_index_write(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int value);
+int rt_sdca_index_read(struct regmap *map, unsigned int nid,
+ unsigned int reg, unsigned int *value);
+int rt_sdca_index_update_bits(struct regmap *map,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val);
+int rt_sdca_btn_type(unsigned char *buffer);
+int rt_sdca_headset_detect(struct regmap *map, unsigned int entity_id);
+int rt_sdca_button_detect(struct regmap *map, unsigned int entity_id,
+ unsigned int hid_buf_addr, unsigned int hid_id);
+
+#endif /* __RT_SDW_COMMON_H__ */
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
new file mode 100644
index 000000000000..b84dd18ddde9
--- /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 <shumingf@realtek.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBC_CFC:
+ 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" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1011_i2c_id[] = {
+ { "rt1011" },
+ { }
+};
+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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1011.h b/sound/soc/codecs/rt1011.h
new file mode 100644
index 000000000000..4d6e7492d99c
--- /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 000000000000..818b45226b72
--- /dev/null
+++ b/sound/soc/codecs/rt1015.c
@@ -0,0 +1,1193 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1015.c -- RT1015 ALSA SoC audio amplifier driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+//
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/rt1015.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 char * const rt1015_dac_output_vol_select[] = {
+ "immediate",
+ "zero detection + immediate change",
+ "zero detection + inc/dec change",
+ "zero detection + soft inc/dec change",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1015_dac_vol_ctl_enum,
+ RT1015_DAC3, 2, rt1015_dac_output_vol_select);
+
+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),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Control", rt1015_dac_vol_ctl_enum),
+};
+
+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_CBP_CFP:
+ reg_val |= RT1015_TCON_TDM_MS_M;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { }
+};
+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" },
+ { }
+};
+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 <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h
new file mode 100644
index 000000000000..c9f636af7fd1
--- /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 <jack.yu@realtek.com>
+//
+// 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 <sound/rt1015.h>
+
+#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 000000000000..44e7fe3c32da
--- /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 <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+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 000000000000..9f86f071fca8
--- /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 <oder_chiou@realtek.com>
+//
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ reg_val |= RT1016_I2S_MS_M;
+ rt1016->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { }
+};
+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" },
+ { }
+};
+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 <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1016.h b/sound/soc/codecs/rt1016.h
new file mode 100644
index 000000000000..041d6a5a6f46
--- /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 <oder_chiou@realtek.com>
+ *
+ * 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 000000000000..a9c000876be8
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -0,0 +1,822 @@
+// 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 <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_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 const 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1017_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt1017-sdca",
+ .pm = pm_ptr(&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 <derek.fang@realtek.com>");
+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 000000000000..4932b5dbe3c0
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* 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 000000000000..86539c6f6cc1
--- /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 <jack.yu@realtek.com>
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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" },
+ { }
+};
+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" },
+ { }
+};
+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 <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1019.h b/sound/soc/codecs/rt1019.h
new file mode 100644
index 000000000000..48ba15efb48d
--- /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 000000000000..26b7382f97ef
--- /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 <shumingf@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ reg_val |= RT1305_SEL_I2S_OUT_MODE_M;
+ rt1305->master = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { "10EC1306" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1305_i2c_id[] = {
+ { "rt1305" },
+ { "rt1306" },
+ { }
+};
+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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1305.h b/sound/soc/codecs/rt1305.h
new file mode 100644
index 000000000000..026f74eb6815
--- /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 <shumingf@realtek.com>
+ */
+
+#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 000000000000..b6c224832a43
--- /dev/null
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -0,0 +1,817 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt1308-sdw.c -- rt1308 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#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_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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume)
+ RUNTIME_PM_OPS(rt1308_dev_suspend, rt1308_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1308_sdw_driver = {
+ .driver = {
+ .name = "rt1308",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+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 000000000000..f816c73e247e
--- /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 000000000000..df50b38c24b9
--- /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 <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBC_CFC:
+ 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" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
+#endif
+
+static const struct i2c_device_id rt1308_i2c_id[] = {
+ { "rt1308" },
+ { }
+};
+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 <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt1308.h b/sound/soc/codecs/rt1308.h
new file mode 100644
index 000000000000..d3a0f91630ca
--- /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 <derek.fang@realtek.com>
+ *
+ */
+
+#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 000000000000..01a977398864
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1316-sdw.c -- rt1316 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#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_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, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Could not allocate bq_params memory\n", __func__);
+ 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, "%s: Could not read list of realtek,bq-params\n", __func__);
+ }
+ }
+
+ 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 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 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume)
+ RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1316_sdw_driver = {
+ .driver = {
+ .name = "rt1316-sdca",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
new file mode 100644
index 000000000000..dc1bfe40edd3
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* 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 000000000000..70db5450d6d2
--- /dev/null
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -0,0 +1,868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318-sdw.c -- rt1318 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2022 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#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_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, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Rate %d is not supported\n",
+ __func__, 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 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 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume)
+ RUNTIME_PM_OPS(rt1318_dev_suspend, rt1318_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1318_sdw_driver = {
+ .driver = {
+ .name = "rt1318-sdca",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h
new file mode 100644
index 000000000000..86e83d63a017
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* 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/rt1318.c b/sound/soc/codecs/rt1318.c
new file mode 100644
index 000000000000..ae01b2ce630b
--- /dev/null
+++ b/sound/soc/codecs/rt1318.c
@@ -0,0 +1,1353 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1318.c -- RT1318 ALSA SoC audio amplifier driver
+// Author: Jack Yu <jack.yu@realtek.com>
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt1318.h>
+
+#include "rt1318.h"
+
+static const struct reg_sequence init_list[] = {
+ { 0x0000C000, 0x01},
+ { 0x0000F20D, 0x00},
+ { 0x0000F212, 0x3E},
+ { 0x0000C001, 0x02},
+ { 0x0000C003, 0x22},
+ { 0x0000C004, 0x44},
+ { 0x0000C005, 0x44},
+ { 0x0000C007, 0x64},
+ { 0x0000C00E, 0xE7},
+ { 0x0000F223, 0x7F},
+ { 0x0000F224, 0xDB},
+ { 0x0000F225, 0xEE},
+ { 0x0000F226, 0x3F},
+ { 0x0000F227, 0x0F},
+ { 0x0000F21A, 0x78},
+ { 0x0000F242, 0x3C},
+ { 0x0000C120, 0x40},
+ { 0x0000C125, 0x03},
+ { 0x0000C321, 0x0A},
+ { 0x0000C200, 0xD8},
+ { 0x0000C201, 0x27},
+ { 0x0000C202, 0x0F},
+ { 0x0000C400, 0x0E},
+ { 0x0000C401, 0x43},
+ { 0x0000C402, 0xE0},
+ { 0x0000C403, 0x00},
+ { 0x0000C404, 0x4C},
+ { 0x0000C406, 0x40},
+ { 0x0000C407, 0x02},
+ { 0x0000C408, 0x3F},
+ { 0x0000C300, 0x01},
+ { 0x0000C125, 0x03},
+ { 0x0000DF00, 0x10},
+ { 0x0000F20B, 0x2A},
+ { 0x0000DF5F, 0x01},
+ { 0x0000DF60, 0xA7},
+ { 0x0000C203, 0x84},
+ { 0x0000C206, 0x78},
+ { 0x0000F10A, 0x09},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x03},
+ { 0x0000F109, 0xE0},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x04},
+ { 0x0000F109, 0x65},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F104, 0xF4},
+ { 0x0000F105, 0x02},
+ { 0x0000F109, 0x30},
+ { 0x0000F10B, 0x5C},
+ { 0x0000E706, 0x0F},
+ { 0x0000E707, 0x30},
+ { 0x0000E806, 0x0F},
+ { 0x0000E807, 0x30},
+ { 0x0000CE04, 0x03},
+ { 0x0000CE05, 0x5F},
+ { 0x0000CE06, 0xA2},
+ { 0x0000CE07, 0x6B},
+ { 0x0000CF04, 0x03},
+ { 0x0000CF05, 0x5F},
+ { 0x0000CF06, 0xA2},
+ { 0x0000CF07, 0x6B},
+ { 0x0000CE60, 0xE3},
+ { 0x0000C130, 0x51},
+ { 0x0000E000, 0xA8},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x04},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x41001888, 0x00},
+ { 0x0000C121, 0x0B},
+ { 0x0000F102, 0x00},
+ { 0x0000F103, 0x00},
+ { 0x0000F104, 0xF5},
+ { 0x0000F105, 0x23},
+ { 0x0000F109, 0x00},
+ { 0x0000F10A, 0x0B},
+ { 0x0000F10B, 0x4C},
+ { 0x0000F10B, 0x5C},
+ { 0x0000F800, 0x20},
+ { 0x0000CA00, 0x80},
+ { 0x0000CA10, 0x00},
+ { 0x0000CA02, 0x78},
+ { 0x0000CA12, 0x78},
+ { 0x0000ED00, 0x90},
+ { 0x0000E604, 0x00},
+ { 0x0000DB00, 0x0C},
+ { 0x0000DD00, 0x0C},
+ { 0x0000DC19, 0x00},
+ { 0x0000DC1A, 0x6A},
+ { 0x0000DC1B, 0xAA},
+ { 0x0000DC1C, 0xAB},
+ { 0x0000DC1D, 0x00},
+ { 0x0000DC1E, 0x16},
+ { 0x0000DC1F, 0xDB},
+ { 0x0000DC20, 0x6D},
+ { 0x0000DE19, 0x00},
+ { 0x0000DE1A, 0x6A},
+ { 0x0000DE1B, 0xAA},
+ { 0x0000DE1C, 0xAB},
+ { 0x0000DE1D, 0x00},
+ { 0x0000DE1E, 0x16},
+ { 0x0000DE1F, 0xDB},
+ { 0x0000DE20, 0x6D},
+ { 0x0000DB32, 0x00},
+ { 0x0000DD32, 0x00},
+ { 0x0000DB33, 0x0A},
+ { 0x0000DD33, 0x0A},
+ { 0x0000DB34, 0x1A},
+ { 0x0000DD34, 0x1A},
+ { 0x0000DB15, 0xEF},
+ { 0x0000DD15, 0xEF},
+ { 0x0000DB17, 0xEF},
+ { 0x0000DD17, 0xEF},
+ { 0x0000DB94, 0x70},
+ { 0x0000DD94, 0x70},
+ { 0x0000DB19, 0x40},
+ { 0x0000DD19, 0x40},
+ { 0x0000DB12, 0xC0},
+ { 0x0000DD12, 0xC0},
+ { 0x0000DB00, 0x4C},
+ { 0x0000DB04, 0x05},
+ { 0x0000DB05, 0x03},
+ { 0x0000DD04, 0x05},
+ { 0x0000DD05, 0x03},
+ { 0x0000DBBB, 0x09},
+ { 0x0000DBBC, 0x30},
+ { 0x0000DBBD, 0xF0},
+ { 0x0000DBBE, 0xF1},
+ { 0x0000DDBB, 0x09},
+ { 0x0000DDBC, 0x30},
+ { 0x0000DDBD, 0xF0},
+ { 0x0000DDBE, 0xF1},
+ { 0x0000DB01, 0x79},
+ { 0x0000DD01, 0x79},
+ { 0x0000DB08, 0x40},
+ { 0x0000DD08, 0x40},
+ { 0x0000DC52, 0xEF},
+ { 0x0000DE52, 0xEF},
+ { 0x0000DB00, 0xCC},
+ { 0x0000CC2C, 0x00},
+ { 0x0000CC2D, 0x2A},
+ { 0x0000CC2E, 0x83},
+ { 0x0000CC2F, 0xA8},
+ { 0x0000CD2C, 0x00},
+ { 0x0000CD2D, 0x2A},
+ { 0x0000CD2E, 0x83},
+ { 0x0000CD2F, 0xA8},
+ { 0x0000CC24, 0x00},
+ { 0x0000CC25, 0x51},
+ { 0x0000CC26, 0xEB},
+ { 0x0000CC27, 0x85},
+ { 0x0000CD24, 0x00},
+ { 0x0000CD25, 0x51},
+ { 0x0000CD26, 0xEB},
+ { 0x0000CD27, 0x85},
+ { 0x0000CC20, 0x00},
+ { 0x0000CC21, 0x00},
+ { 0x0000CC22, 0x43},
+ { 0x0000CD20, 0x00},
+ { 0x0000CD21, 0x00},
+ { 0x0000CD22, 0x43},
+ { 0x0000CC16, 0x0F},
+ { 0x0000CC17, 0x00},
+ { 0x0000CD16, 0x0F},
+ { 0x0000CD17, 0x00},
+ { 0x0000CC29, 0x5D},
+ { 0x0000CC2A, 0xC0},
+ { 0x0000CD29, 0x5D},
+ { 0x0000CD2A, 0xC0},
+ { 0x0000CC31, 0x20},
+ { 0x0000CC32, 0x00},
+ { 0x0000CC33, 0x00},
+ { 0x0000CC34, 0x00},
+ { 0x0000CD31, 0x20},
+ { 0x0000CD32, 0x00},
+ { 0x0000CD33, 0x00},
+ { 0x0000CD34, 0x00},
+ { 0x0000CC36, 0x79},
+ { 0x0000CC37, 0x99},
+ { 0x0000CC38, 0x99},
+ { 0x0000CC39, 0x99},
+ { 0x0000CD36, 0x79},
+ { 0x0000CD37, 0x99},
+ { 0x0000CD38, 0x99},
+ { 0x0000CD39, 0x99},
+ { 0x0000CC09, 0x00},
+ { 0x0000CC0A, 0x07},
+ { 0x0000CC0B, 0x5F},
+ { 0x0000CC0C, 0x6F},
+ { 0x0000CD09, 0x00},
+ { 0x0000CD0A, 0x07},
+ { 0x0000CD0B, 0x5F},
+ { 0x0000CD0C, 0x6F},
+ { 0x0000CC0E, 0x00},
+ { 0x0000CC0F, 0x03},
+ { 0x0000CC10, 0xAF},
+ { 0x0000CC11, 0xB7},
+ { 0x0000CD0E, 0x00},
+ { 0x0000CD0F, 0x03},
+ { 0x0000CD10, 0xAF},
+ { 0x0000CD11, 0xB7},
+ { 0x0000CCD6, 0x00},
+ { 0x0000CCD7, 0x03},
+ { 0x0000CDD6, 0x00},
+ { 0x0000CDD7, 0x03},
+ { 0x0000CCD8, 0x00},
+ { 0x0000CCD9, 0x03},
+ { 0x0000CDD8, 0x00},
+ { 0x0000CDD9, 0x03},
+ { 0x0000CCDA, 0x00},
+ { 0x0000CCDB, 0x03},
+ { 0x0000CDDA, 0x00},
+ { 0x0000CDDB, 0x03},
+ { 0x0000C320, 0x20},
+ { 0x0000C203, 0x9C},
+};
+
+static const struct reg_default rt1318_reg[] = {
+ { 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 },
+ { 0xc130, 0x59 },
+ { 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 },
+ { 0xc400, 0x00 },
+ { 0xc401, 0x00 },
+ { 0xc402, 0x00 },
+ { 0xc403, 0x00 },
+ { 0xc404, 0x00 },
+ { 0xc405, 0x00 },
+ { 0xc406, 0x00 },
+ { 0xc407, 0x00 },
+ { 0xc408, 0x00 },
+ { 0xc410, 0x04 },
+ { 0xc430, 0x00 },
+ { 0xc431, 0x00 },
+ { 0xca00, 0x10 },
+ { 0xca01, 0x00 },
+ { 0xca02, 0x0b },
+ { 0xca10, 0x10 },
+ { 0xca11, 0x00 },
+ { 0xca12, 0x0b },
+ { 0xce04, 0x08 },
+ { 0xce05, 0x00 },
+ { 0xce06, 0x00 },
+ { 0xce07, 0x00 },
+ { 0xce60, 0x63 },
+ { 0xcf04, 0x08 },
+ { 0xcf05, 0x00 },
+ { 0xcf06, 0x00 },
+ { 0xcf07, 0x00 },
+ { 0xdb00, 0x00 },
+ { 0xdb08, 0x40 },
+ { 0xdb12, 0x00 },
+ { 0xdb35, 0x00 },
+ { 0xdbb5, 0x00 },
+ { 0xdbb6, 0x40 },
+ { 0xdbb7, 0x00 },
+ { 0xdbb8, 0x00 },
+ { 0xdbc5, 0x00 },
+ { 0xdbc6, 0x00 },
+ { 0xdbc7, 0x00 },
+ { 0xdbc8, 0x00 },
+ { 0xdd08, 0x40 },
+ { 0xdd12, 0x00 },
+ { 0xdd35, 0x00 },
+ { 0xddb5, 0x00 },
+ { 0xddb6, 0x40 },
+ { 0xddb7, 0x00 },
+ { 0xddb8, 0x00 },
+ { 0xddc5, 0x00 },
+ { 0xddc6, 0x00 },
+ { 0xddc7, 0x00 },
+ { 0xddc8, 0x00 },
+ { 0xdd93, 0x00 },
+ { 0xdd94, 0x64 },
+ { 0xdf00, 0x00 },
+ { 0xdf5f, 0x00 },
+ { 0xdf60, 0x00 },
+ { 0xe000, 0x08 },
+ { 0xe300, 0xa0 },
+ { 0xe400, 0x22 },
+ { 0xe706, 0x2f },
+ { 0xe707, 0x2f },
+ { 0xe806, 0x2f },
+ { 0xe807, 0x2f },
+ { 0xea00, 0x43 },
+ { 0xed00, 0x80 },
+ { 0xed01, 0x0f },
+ { 0xed02, 0xff },
+ { 0xed03, 0x00 },
+ { 0xed04, 0x00 },
+ { 0xed05, 0x0f },
+ { 0xed06, 0xff },
+ { 0xf010, 0x10 },
+ { 0xf011, 0xec },
+ { 0xf012, 0x68 },
+ { 0xf013, 0x21 },
+ { 0xf102, 0x00 },
+ { 0xf103, 0x00 },
+ { 0xf104, 0x00 },
+ { 0xf105, 0x00 },
+ { 0xf106, 0x00 },
+ { 0xf107, 0x00 },
+ { 0xf108, 0x00 },
+ { 0xf109, 0x00 },
+ { 0xf10a, 0x03 },
+ { 0xf10b, 0x40 },
+ { 0xf20b, 0x28 },
+ { 0xf20d, 0x00 },
+ { 0xf212, 0x00 },
+ { 0xf21a, 0x00 },
+ { 0xf223, 0x40 },
+ { 0xf224, 0x00 },
+ { 0xf225, 0x00 },
+ { 0xf226, 0x00 },
+ { 0xf227, 0x00 },
+ { 0xf242, 0x0c },
+ { 0xf800, 0x00 },
+ { 0xf801, 0x12 },
+ { 0xf802, 0xe0 },
+ { 0xf803, 0x2f },
+ { 0xf804, 0x00 },
+ { 0xf805, 0x00 },
+ { 0xf806, 0x07 },
+ { 0xf807, 0xff },
+};
+
+static bool rt1318_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ 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:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static bool rt1318_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc00f:
+ case 0xc120 ... 0xc130:
+ case 0xc200 ... 0xc20e:
+ case 0xc300 ... 0xc303:
+ case 0xc320 ... 0xc322:
+ case 0xc400 ... 0xc408:
+ 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 0xe000:
+ case 0xe300:
+ case 0xe400:
+ case 0xe706 ... 0xe707:
+ case 0xe806 ... 0xe807:
+ case 0xea00:
+ case 0xeb00 ... 0xebcc:
+ case 0xec00 ... 0xecb9:
+ case 0xed00 ... 0xed06:
+ case 0xf010 ... 0xf014:
+ case 0xf102 ... 0xf10b:
+ case 0xf20b:
+ case 0xf20d ... 0xf242:
+ case 0xf800 ... 0xf807:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_HIGH);
+ break;
+
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1318->regmap, RT1318_PWR_STA1,
+ RT1318_PDB_CTRL_MASK, RT1318_PDB_CTRL_LOW);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1318_dvol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->rt1318_dvol = ucontrol->value.integer.value[0];
+
+ if (rt1318->rt1318_dvol <= RT1318_DVOL_STEP && rt1318->rt1318_dvol >= 0) {
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_L_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_8,
+ rt1318->rt1318_dvol >> 8);
+ regmap_write(rt1318->regmap, RT1318_DA_VOL_R_1_7,
+ rt1318->rt1318_dvol & 0xff);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rt1318_dvol_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt1318->rt1318_dvol;
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new rt1318_snd_controls[] = {
+ SOC_SINGLE_EXT("Amp Playback Volume", SND_SOC_NOPM, 0, 383, 0,
+ rt1318_dvol_get, rt1318_dvol_put),
+};
+
+static const struct snd_soc_dapm_widget rt1318_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ /* DACs */
+ SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0,
+ rt1318_dac_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("Amp"),
+};
+
+static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
+ {"DAC", NULL, "AIF1RX"},
+ {"Amp", NULL, "DAC"},
+};
+
+static int rt1318_get_clk_info(int sclk, int rate)
+{
+ int i, pd[] = {1, 2, 4, 8, 16, 24};
+
+ 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 rt1318_clk_ip_info(struct snd_soc_component *component, int lrclk)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ switch (lrclk) {
+ case RT1318_LRCLK_48000:
+ case RT1318_LRCLK_44100:
+ case RT1318_LRCLK_16000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON4);
+ break;
+ case RT1318_LRCLK_96000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON2);
+ break;
+ case RT1318_LRCLK_192000:
+ regmap_update_bits(rt1318->regmap, RT1318_SRC_TCON,
+ RT1318_SRCIN_F12288_MASK | RT1318_SRCIN_DACLK_MASK,
+ RT1318_SRCIN_TCON4 | RT1318_DACLK_TCON1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock rate.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt1318_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_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int data_len = 0, ch_len = 0;
+ int pre_div, ret;
+
+ rt1318->lrck = params_rate(params);
+ pre_div = rt1318_get_clk_info(rt1318->sysclk, rt1318->lrck);
+ if (pre_div < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+ ret = rt1318_clk_ip_info(component, rt1318->lrck);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupported clock setting\n");
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ break;
+ case 20:
+ data_len = RT1318_I2S_DL_20;
+ ch_len = RT1318_I2S_DL_20;
+ break;
+ case 24:
+ data_len = RT1318_I2S_DL_24;
+ ch_len = RT1318_I2S_DL_24;
+ break;
+ case 32:
+ data_len = RT1318_I2S_DL_32;
+ ch_len = RT1318_I2S_DL_32;
+ break;
+ case 8:
+ data_len = RT1318_I2S_DL_8;
+ ch_len = RT1318_I2S_DL_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_CLK2,
+ RT1318_DIV_AP_MASK | RT1318_DIV_DAMOD_MASK,
+ pre_div << RT1318_DIV_AP_SFT |
+ pre_div << RT1318_DIV_DAMOD_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK3,
+ RT1318_AD_STO1_MASK | RT1318_AD_STO2_MASK,
+ pre_div << RT1318_AD_STO1_SFT |
+ pre_div << RT1318_AD_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK4,
+ RT1318_AD_ANA_STO1_MASK | RT1318_AD_ANA_STO2_MASK,
+ pre_div << RT1318_AD_ANA_STO1_SFT |
+ pre_div << RT1318_AD_ANA_STO2_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK5,
+ RT1318_DIV_FIFO_IN_MASK | RT1318_DIV_FIFO_OUT_MASK,
+ pre_div << RT1318_DIV_FIFO_IN_SFT |
+ pre_div << RT1318_DIV_FIFO_OUT_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK6,
+ RT1318_DIV_NLMS_MASK | RT1318_DIV_AD_MONO_MASK |
+ RT1318_DIV_POST_G_MASK, pre_div << RT1318_DIV_NLMS_SFT |
+ pre_div << RT1318_DIV_AD_MONO_SFT |
+ pre_div << RT1318_DIV_POST_G_SFT);
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_DL_MASK, data_len << RT1318_I2S_DL_SFT);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK,
+ ch_len << RT1318_I2S_TX_CHL_SFT |
+ ch_len << RT1318_I2S_RX_CHL_SFT);
+
+ return 0;
+}
+
+static int rt1318_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(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 |= RT1318_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 |= RT1318_FMT_LEFT_J;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT1318_FMT_PCM_A_R;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT1318_FMT_PCM_B_R;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_I2S_FMT_MASK, reg_val);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL1,
+ RT1318_TDM_BCLK_MASK, reg_val2);
+
+ return 0;
+}
+
+static int rt1318_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ int reg_val = 0;
+
+ if (freq == rt1318->sysclk && clk_id == rt1318->sysclk_src)
+ return 0;
+
+ switch (clk_id) {
+ case RT1318_SCLK_S_BCLK:
+ reg_val |= RT1318_SYSCLK_BCLK;
+ break;
+ case RT1318_SCLK_S_SDW:
+ reg_val |= RT1318_SYSCLK_SDW;
+ break;
+ case RT1318_SCLK_S_PLL2F:
+ reg_val |= RT1318_SYSCLK_PLL2F;
+ break;
+ case RT1318_SCLK_S_PLL2B:
+ reg_val |= RT1318_SYSCLK_PLL2B;
+ break;
+ case RT1318_SCLK_S_MCLK:
+ reg_val |= RT1318_SYSCLK_MCLK;
+ break;
+ case RT1318_SCLK_S_RC0:
+ reg_val |= RT1318_SYSCLK_RC1;
+ break;
+ case RT1318_SCLK_S_RC1:
+ reg_val |= RT1318_SYSCLK_RC2;
+ break;
+ case RT1318_SCLK_S_RC2:
+ reg_val |= RT1318_SYSCLK_RC3;
+ break;
+ default:
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ rt1318->sysclk = freq;
+ rt1318->sysclk_src = clk_id;
+ dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_SYSCLK_SEL_MASK, reg_val);
+
+ return 0;
+}
+
+static const struct pll_calc_map pll_preset_table[] = {
+ {512000, 4096000, 22, 190, 0, true, false},
+ {1024000, 4096000, 22, 94, 0, true, false},
+ {1024000, 16384000, 4, 190, 0, true, false},
+ {1411200, 11289600, 6, 62, 0, true, false},
+ {1536000, 12288000, 6, 62, 0, true, false},
+ {2822400, 11289600, 6, 62, 0, true, false},
+ {2822400, 45158400, 0, 62, 0, true, false},
+ {2822400, 49152000, 0, 62, 0, true, false},
+ {3072000, 12288000, 6, 62, 0, true, false},
+ {3072000, 24576000, 2, 62, 0, true, false},
+ {3072000, 49152000, 0, 62, 0, true, false},
+ {6144000, 24576000, 2, 94, 4, false, false},
+ {6144000, 49152000, 0, 30, 0, true, false},
+ {6144000, 98304000, 0, 94, 4, false, true},
+ {12288000, 49152000, 0, 62, 6, false, false},
+};
+
+static int rt1318_pll_calc(const unsigned int freq_in,
+ const unsigned int freq_out, struct rt1318_pll_code *pll_code)
+{
+ int max_n = RT1318_PLL_N_MAX, max_m = RT1318_PLL_M_MAX;
+ int i, k, red, n_t, pll_out, in_t, out_t;
+ int n = 0, m = 0, m_t = 0;
+ int red_t = abs(freq_out - freq_in);
+ bool m_bypass = false, k_bypass = false;
+
+ if (RT1318_PLL_INP_MAX < freq_in || RT1318_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;
+ goto code_find;
+ }
+ }
+
+ k = 100000000 / freq_out - 2;
+ if (k > RT1318_PLL_K_MAX)
+ k = RT1318_PLL_K_MAX;
+ if (k < 0) {
+ k = 0;
+ k_bypass = true;
+ }
+ for (n_t = 0; n_t <= max_n; n_t++) {
+ in_t = freq_in / (k_bypass ? 1 : (k + 2));
+ pll_out = freq_out / (n_t + 2);
+ if (in_t < 0)
+ continue;
+ if (in_t == pll_out) {
+ m_bypass = true;
+ n = n_t;
+ goto code_find;
+ }
+ red = abs(in_t - pll_out);
+ if (red < red_t) {
+ m_bypass = true;
+ n = n_t;
+ m = m_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);
+ red = abs(out_t - pll_out);
+ if (red < red_t) {
+ m_bypass = false;
+ n = n_t;
+ m = m_t;
+ if (red == 0)
+ goto code_find;
+ red_t = red;
+ }
+ }
+ }
+ pr_debug("Only get approximation about PLL\n");
+
+code_find:
+
+ 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;
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ struct rt1318_pll_code pll_code;
+ int ret;
+
+ if (!freq_in || !freq_out) {
+ dev_dbg(component->dev, "PLL disabled\n");
+ rt1318->pll_in = 0;
+ rt1318->pll_out = 0;
+ return 0;
+ }
+
+ if (source == rt1318->pll_src && freq_in == rt1318->pll_in &&
+ freq_out == rt1318->pll_out)
+ return 0;
+
+ switch (source) {
+ case RT1318_PLL_S_BCLK0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK0);
+ break;
+ case RT1318_PLL_S_BCLK1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_BCLK1);
+ break;
+ case RT1318_PLL_S_RC:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_RC);
+ break;
+ case RT1318_PLL_S_MCLK:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_MCLK);
+ break;
+ case RT1318_PLL_S_SDW_IN_PLL:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW1);
+ break;
+ case RT1318_PLL_S_SDW_0:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW2);
+ break;
+ case RT1318_PLL_S_SDW_1:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW3);
+ break;
+ case RT1318_PLL_S_SDW_2:
+ regmap_update_bits(rt1318->regmap, RT1318_CLK1,
+ RT1318_PLLIN_MASK, RT1318_PLLIN_SDW4);
+ break;
+ default:
+ dev_err(component->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rt1318_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(component->dev, "Unsupport 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_update_bits(rt1318->regmap, RT1318_PLL1_K,
+ RT1318_K_PLL1_MASK, pll_code.k_code);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_M,
+ RT1318_M_PLL1_MASK, (pll_code.m_bp ? 0 : pll_code.m_code));
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_8,
+ RT1318_N_8_PLL1_MASK, pll_code.n_code >> 8);
+ regmap_update_bits(rt1318->regmap, RT1318_PLL1_N_7_0,
+ RT1318_N_7_0_PLL1_MASK, pll_code.n_code);
+
+ rt1318->pll_in = freq_in;
+ rt1318->pll_out = freq_out;
+ rt1318->pll_src = source;
+
+ return 0;
+}
+
+static int rt1318_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 rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+ unsigned int cn = 0, cl = 0, rx_slotnum;
+ int ret = 0, first_bit;
+
+ switch (slots) {
+ case 4:
+ cn |= RT1318_I2S_CH_TX_4CH;
+ cn |= RT1318_I2S_CH_RX_4CH;
+ break;
+ case 6:
+ cn |= RT1318_I2S_CH_TX_6CH;
+ cn |= RT1318_I2S_CH_RX_6CH;
+ break;
+ case 8:
+ cn |= RT1318_I2S_CH_TX_8CH;
+ cn |= RT1318_I2S_CH_RX_8CH;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ cl |= RT1318_I2S_TX_CHL_20;
+ cl |= RT1318_I2S_RX_CHL_20;
+ break;
+ case 24:
+ cl |= RT1318_I2S_TX_CHL_24;
+ cl |= RT1318_I2S_RX_CHL_24;
+ break;
+ case 32:
+ cl |= RT1318_I2S_TX_CHL_32;
+ cl |= RT1318_I2S_RX_CHL_32;
+ break;
+ case 8:
+ cl |= RT1318_I2S_TX_CHL_8;
+ cl |= RT1318_I2S_RX_CHL_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_;
+ }
+
+ first_bit = __ffs(rx_mask);
+ switch (first_bit) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ (first_bit << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ ((first_bit + 1) << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ regmap_update_bits(rt1318->regmap,
+ RT1318_TDM_CTRL9,
+ RT1318_TDM_I2S_TX_L_DAC1_1_MASK |
+ RT1318_TDM_I2S_TX_R_DAC1_1_MASK,
+ ((first_bit - 1) << RT1318_TDM_I2S_TX_L_DAC1_1_SFT) |
+ (first_bit << RT1318_TDM_I2S_TX_R_DAC1_1_SFT));
+ break;
+ default:
+ ret = -EINVAL;
+ goto _set_tdm_err_;
+ }
+
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL2,
+ RT1318_I2S_CH_TX_MASK | RT1318_I2S_CH_RX_MASK, cn);
+ regmap_update_bits(rt1318->regmap, RT1318_TDM_CTRL3,
+ RT1318_I2S_TX_CHL_MASK | RT1318_I2S_RX_CHL_MASK, cl);
+
+_set_tdm_err_:
+ return ret;
+}
+
+static int rt1318_probe(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ rt1318->component = component;
+
+ schedule_work(&rt1318->cali_work);
+ rt1318->rt1318_dvol = RT1318_DVOL_STEP;
+
+ return 0;
+}
+
+static void rt1318_remove(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&rt1318->cali_work);
+}
+
+#ifdef CONFIG_PM
+static int rt1318_suspend(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, true);
+ regcache_mark_dirty(rt1318->regmap);
+ return 0;
+}
+
+static int rt1318_resume(struct snd_soc_component *component)
+{
+ struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1318->regmap, false);
+ regcache_sync(rt1318->regmap);
+ return 0;
+}
+#else
+#define rt1318_suspend NULL
+#define rt1318_resume NULL
+#endif
+
+#define RT1318_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT1318_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 rt1318_aif_dai_ops = {
+ .hw_params = rt1318_hw_params,
+ .set_fmt = rt1318_set_dai_fmt,
+ .set_sysclk = rt1318_set_dai_sysclk,
+ .set_pll = rt1318_set_dai_pll,
+ .set_tdm_slot = rt1318_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver rt1318_dai[] = {
+ {
+ .name = "rt1318-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1318_STEREO_RATES,
+ .formats = RT1318_FORMATS,
+ },
+ .ops = &rt1318_aif_dai_ops,
+ }
+};
+
+static const struct snd_soc_component_driver soc_component_dev_rt1318 = {
+ .probe = rt1318_probe,
+ .remove = rt1318_remove,
+ .suspend = rt1318_suspend,
+ .resume = rt1318_resume,
+ .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),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct regmap_config rt1318_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1318_readable_register,
+ .volatile_reg = rt1318_volatile_register,
+ .max_register = 0x41001888,
+ .reg_defaults = rt1318_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt1318_reg),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct i2c_device_id rt1318_i2c_id[] = {
+ { "rt1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id);
+
+static const struct of_device_id rt1318_of_match[] = {
+ { .compatible = "realtek,rt1318", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt1318_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt1318_acpi_match[] = {
+ { "10EC1318" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt1318_acpi_match);
+#endif
+
+static int rt1318_parse_dt(struct rt1318_priv *rt1318, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,r0_l",
+ &rt1318->pdata.init_r0_l);
+ device_property_read_u32(dev, "realtek,r0_r",
+ &rt1318->pdata.init_r0_r);
+
+ return 0;
+}
+
+static void rt1318_calibration_sequence(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_CLK1, 0x22);
+ regmap_write(rt1318->regmap, RT1318_PLL1_N_7_0, 0x06);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xCC);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x40);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x40);
+ regmap_write(rt1318->regmap, RT1318_SINE_GEN0, 0x20);
+ regmap_write(rt1318->regmap, RT1318_SPK_VOL_TH, 0x00);
+ regmap_write(rt1318->regmap, RT1318_FEEDBACK_PATH, 0x0B);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x1C);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x58);
+ regmap_write(rt1318->regmap, RT1318_TCON_RELATE, 0x78);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xC2);
+}
+
+static void rt1318_r0_calculate(struct rt1318_priv *rt1318)
+{
+ unsigned int r0_l, r0_l_byte0, r0_l_byte1, r0_l_byte2, r0_l_byte3;
+ unsigned int r0_r, r0_r_byte0, r0_r_byte1, r0_r_byte2, r0_r_byte3;
+ unsigned int r0_l_integer, r0_l_factor, r0_r_integer, r0_r_factor;
+ unsigned int format = 16777216; /* 2^24 */
+
+ regmap_read(rt1318->regmap, RT1318_R0_L_24, &r0_l_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_L_23_16, &r0_l_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_L_15_8, &r0_l_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_L_7_0, &r0_l_byte3);
+ r0_l = r0_l_byte0 << 24 | r0_l_byte1 << 16 | r0_l_byte2 << 8 | r0_l_byte3;
+ r0_l_integer = format / r0_l;
+ r0_l_factor = (format * 10) / r0_l - r0_l_integer * 10;
+
+ regmap_read(rt1318->regmap, RT1318_R0_R_24, &r0_r_byte0);
+ regmap_read(rt1318->regmap, RT1318_R0_R_23_16, &r0_r_byte1);
+ regmap_read(rt1318->regmap, RT1318_R0_R_15_8, &r0_r_byte2);
+ regmap_read(rt1318->regmap, RT1318_R0_R_7_0, &r0_r_byte3);
+ r0_r = r0_r_byte0 << 24 | r0_r_byte1 << 16 | r0_r_byte2 << 8 | r0_r_byte3;
+ r0_r_integer = format / r0_r;
+ r0_r_factor = (format * 10) / r0_r - r0_r_integer * 10;
+
+ dev_dbg(rt1318->component->dev, "r0_l_ch:%d.%d ohm\n", r0_l_integer, r0_l_factor);
+ dev_dbg(rt1318->component->dev, "r0_r_ch:%d.%d ohm\n", r0_r_integer, r0_r_factor);
+}
+
+static void rt1318_r0_restore(struct rt1318_priv *rt1318)
+{
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_24,
+ (rt1318->pdata.init_r0_l >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_23_16,
+ (rt1318->pdata.init_r0_l >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_15_8,
+ (rt1318->pdata.init_r0_l >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_L_7_0,
+ (rt1318->pdata.init_r0_l >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_24,
+ (rt1318->pdata.init_r0_r >> 24) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_23_16,
+ (rt1318->pdata.init_r0_r >> 16) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_15_8,
+ (rt1318->pdata.init_r0_r >> 8) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_PRE_R0_R_7_0,
+ (rt1318->pdata.init_r0_r >> 0) & 0xff);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_L, 0x80);
+ regmap_write(rt1318->regmap, RT1318_STP_SEL_R, 0x80);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_L_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_R0_CMP_R_FLAG, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_L, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_R0_EN_R, 0xc0);
+ regmap_write(rt1318->regmap, RT1318_STP_TEMP_L, 0xcc);
+ regmap_write(rt1318->regmap, RT1318_TCON, 0x9c);
+}
+
+static int rt1318_calibrate(struct rt1318_priv *rt1318)
+{
+ int chk_cnt = 30, count = 0;
+ int val, val2;
+
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x1);
+ usleep_range(0, 10000);
+ rt1318_calibration_sequence(rt1318);
+
+ while (count < chk_cnt) {
+ msleep(100);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ val = (val >> 1) & 0x1;
+ val2 = (val2 >> 1) & 0x1;
+ if (val & val2) {
+ dev_dbg(rt1318->component->dev, "Calibration done.\n");
+ break;
+ }
+ count++;
+ if (count == chk_cnt) {
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ return RT1318_R0_CALIB_NOT_DONE;
+ }
+ }
+ regmap_write(rt1318->regmap, RT1318_PWR_STA1, 0x0);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_L_FLAG, &val);
+ regmap_read(rt1318->regmap, RT1318_R0_CMP_R_FLAG, &val2);
+ if ((val & 0x1) & (val2 & 0x1))
+ return RT1318_R0_IN_RANGE;
+ else
+ return RT1318_R0_OUT_OF_RANGE;
+}
+
+static void rt1318_calibration_work(struct work_struct *work)
+{
+ struct rt1318_priv *rt1318 =
+ container_of(work, struct rt1318_priv, cali_work);
+ int ret;
+
+ if (rt1318->pdata.init_r0_l && rt1318->pdata.init_r0_r)
+ rt1318_r0_restore(rt1318);
+ else {
+ ret = rt1318_calibrate(rt1318);
+ if (ret == RT1318_R0_IN_RANGE)
+ rt1318_r0_calculate(rt1318);
+ dev_dbg(rt1318->component->dev, "Calibrate R0 result:%d\n", ret);
+ }
+}
+
+static int rt1318_i2c_probe(struct i2c_client *i2c)
+{
+ struct rt1318_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt1318_priv *rt1318;
+ int ret, val, val2, dev_id;
+
+ rt1318 = devm_kzalloc(&i2c->dev, sizeof(struct rt1318_priv),
+ GFP_KERNEL);
+ if (!rt1318)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, rt1318);
+
+ if (pdata)
+ rt1318->pdata = *pdata;
+ else
+ rt1318_parse_dt(rt1318, &i2c->dev);
+
+ rt1318->regmap = devm_regmap_init_i2c(i2c, &rt1318_regmap);
+ if (IS_ERR(rt1318->regmap)) {
+ ret = PTR_ERR(rt1318->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt1318->regmap, RT1318_DEV_ID1, &val);
+ regmap_read(rt1318->regmap, RT1318_DEV_ID2, &val2);
+ dev_id = (val << 8) | val2;
+ if (dev_id != 0x6821) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt1318\n",
+ dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_register_patch(rt1318->regmap, init_list,
+ ARRAY_SIZE(init_list));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ INIT_WORK(&rt1318->cali_work, rt1318_calibration_work);
+
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt1318, rt1318_dai, ARRAY_SIZE(rt1318_dai));
+}
+
+static struct i2c_driver rt1318_i2c_driver = {
+ .driver = {
+ .name = "rt1318",
+ .of_match_table = of_match_ptr(rt1318_of_match),
+ .acpi_match_table = ACPI_PTR(rt1318_acpi_match),
+ },
+ .probe = rt1318_i2c_probe,
+ .id_table = rt1318_i2c_id,
+};
+module_i2c_driver(rt1318_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT1318 driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1318.h b/sound/soc/codecs/rt1318.h
new file mode 100644
index 000000000000..cec40b484216
--- /dev/null
+++ b/sound/soc/codecs/rt1318.h
@@ -0,0 +1,342 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1318.h -- Platform data for RT1318
+ *
+ * Copyright 2024 Realtek Semiconductor Corp.
+ */
+#include <sound/rt1318.h>
+
+#ifndef __RT1318_H__
+#define __RT1318_H__
+
+struct rt1318_priv {
+ struct snd_soc_component *component;
+ struct rt1318_platform_data pdata;
+ struct work_struct cali_work;
+ struct regmap *regmap;
+
+ unsigned int r0_l_integer;
+ unsigned int r0_l_factor;
+ unsigned int r0_r_integer;
+ unsigned int r0_r_factor;
+ int rt1318_init;
+ int rt1318_dvol;
+ int sysclk_src;
+ int sysclk;
+ int lrck;
+ int bclk;
+ int master;
+ int pll_src;
+ int pll_in;
+ int pll_out;
+};
+
+#define RT1318_PLL_INP_MAX 40000000
+#define RT1318_PLL_INP_MIN 256000
+#define RT1318_PLL_N_MAX 0x1ff
+#define RT1318_PLL_K_MAX 0x1f
+#define RT1318_PLL_M_MAX 0x1f
+
+#define RT1318_LRCLK_192000 192000
+#define RT1318_LRCLK_96000 96000
+#define RT1318_LRCLK_48000 48000
+#define RT1318_LRCLK_44100 44100
+#define RT1318_LRCLK_16000 16000
+#define RT1318_DVOL_STEP 383
+
+#define RT1318_CLK1 0xc001
+#define RT1318_CLK2 0xc003
+#define RT1318_CLK3 0xc004
+#define RT1318_CLK4 0xc005
+#define RT1318_CLK5 0xc006
+#define RT1318_CLK6 0xc007
+#define RT1318_CLK7 0xc008
+#define RT1318_PWR_STA1 0xc121
+#define RT1318_SPK_VOL_TH 0xc130
+#define RT1318_TCON 0xc203
+#define RT1318_SRC_TCON 0xc204
+#define RT1318_TCON_RELATE 0xc206
+#define RT1318_DA_VOL_L_8 0xc20b
+#define RT1318_DA_VOL_L_1_7 0xc20c
+#define RT1318_DA_VOL_R_8 0xc20d
+#define RT1318_DA_VOL_R_1_7 0xc20e
+#define RT1318_FEEDBACK_PATH 0xc321
+#define RT1318_STP_TEMP_L 0xdb00
+#define RT1318_STP_SEL_L 0xdb08
+#define RT1318_STP_R0_EN_L 0xdb12
+#define RT1318_R0_CMP_L_FLAG 0xdb35
+#define RT1318_PRE_R0_L_24 0xdbb5
+#define RT1318_PRE_R0_L_23_16 0xdbb6
+#define RT1318_PRE_R0_L_15_8 0xdbb7
+#define RT1318_PRE_R0_L_7_0 0xdbb8
+#define RT1318_R0_L_24 0xdbc5
+#define RT1318_R0_L_23_16 0xdbc6
+#define RT1318_R0_L_15_8 0xdbc7
+#define RT1318_R0_L_7_0 0xdbc8
+#define RT1318_STP_SEL_R 0xdd08
+#define RT1318_STP_R0_EN_R 0xdd12
+#define RT1318_R0_CMP_R_FLAG 0xdd35
+#define RT1318_PRE_R0_R_24 0xddb5
+#define RT1318_PRE_R0_R_23_16 0xddb6
+#define RT1318_PRE_R0_R_15_8 0xddb7
+#define RT1318_PRE_R0_R_7_0 0xddb8
+#define RT1318_R0_R_24 0xddc5
+#define RT1318_R0_R_23_16 0xddc6
+#define RT1318_R0_R_15_8 0xddc7
+#define RT1318_R0_R_7_0 0xddc8
+#define RT1318_DEV_ID1 0xf012
+#define RT1318_DEV_ID2 0xf013
+#define RT1318_PLL1_K 0xf20d
+#define RT1318_PLL1_M 0xf20f
+#define RT1318_PLL1_N_8 0xf211
+#define RT1318_PLL1_N_7_0 0xf212
+#define RT1318_SINE_GEN0 0xf800
+#define RT1318_TDM_CTRL1 0xf900
+#define RT1318_TDM_CTRL2 0xf901
+#define RT1318_TDM_CTRL3 0xf902
+#define RT1318_TDM_CTRL9 0xf908
+
+
+/* Clock-1 (0xC001) */
+#define RT1318_PLLIN_MASK (0x7 << 4)
+#define RT1318_PLLIN_BCLK0 (0x0 << 4)
+#define RT1318_PLLIN_BCLK1 (0x1 << 4)
+#define RT1318_PLLIN_RC (0x2 << 4)
+#define RT1318_PLLIN_MCLK (0x3 << 4)
+#define RT1318_PLLIN_SDW1 (0x4 << 4)
+#define RT1318_PLLIN_SDW2 (0x5 << 4)
+#define RT1318_PLLIN_SDW3 (0x6 << 4)
+#define RT1318_PLLIN_SDW4 (0x7 << 4)
+#define RT1318_SYSCLK_SEL_MASK (0x7 << 0)
+#define RT1318_SYSCLK_BCLK (0x0 << 0)
+#define RT1318_SYSCLK_SDW (0x1 << 0)
+#define RT1318_SYSCLK_PLL2F (0x2 << 0)
+#define RT1318_SYSCLK_PLL2B (0x3 << 0)
+#define RT1318_SYSCLK_MCLK (0x4 << 0)
+#define RT1318_SYSCLK_RC1 (0x5 << 0)
+#define RT1318_SYSCLK_RC2 (0x6 << 0)
+#define RT1318_SYSCLK_RC3 (0x7 << 0)
+/* Clock-2 (0xC003) */
+#define RT1318_DIV_AP_MASK (0x3 << 4)
+#define RT1318_DIV_AP_SFT 4
+#define RT1318_DIV_AP_DIV1 (0x0 << 4)
+#define RT1318_DIV_AP_DIV2 (0x1 << 4)
+#define RT1318_DIV_AP_DIV4 (0x2 << 4)
+#define RT1318_DIV_AP_DIV8 (0x3 << 4)
+#define RT1318_DIV_DAMOD_MASK (0x3 << 0)
+#define RT1318_DIV_DAMOD_SFT 0
+#define RT1318_DIV_DAMOD_DIV1 (0x0 << 0)
+#define RT1318_DIV_DAMOD_DIV2 (0x1 << 0)
+#define RT1318_DIV_DAMOD_DIV4 (0x2 << 0)
+#define RT1318_DIV_DAMOD_DIV8 (0x3 << 0)
+/* Clock-3 (0xC004) */
+#define RT1318_AD_STO1_MASK (0x7 << 4)
+#define RT1318_AD_STO1_SFT 4
+#define RT1318_AD_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_STO2_MASK (0x7 << 0)
+#define RT1318_AD_STO2_SFT 0
+#define RT1318_AD_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_STO2_SFT 0
+/* Clock-4 (0xC005) */
+#define RT1318_AD_ANA_STO1_MASK (0x7 << 4)
+#define RT1318_AD_ANA_STO1_SFT 4
+#define RT1318_AD_ANA_STO1_DIV1 (0x0 << 4)
+#define RT1318_AD_ANA_STO1_DIV2 (0x1 << 4)
+#define RT1318_AD_ANA_STO1_DIV4 (0x2 << 4)
+#define RT1318_AD_ANA_STO1_DIV8 (0x3 << 4)
+#define RT1318_AD_ANA_STO1_DIV16 (0x4 << 4)
+#define RT1318_AD_ANA_STO2_MASK (0x7 << 0)
+#define RT1318_AD_ANA_STO2_DIV1 (0x0 << 0)
+#define RT1318_AD_ANA_STO2_DIV2 (0x1 << 0)
+#define RT1318_AD_ANA_STO2_DIV4 (0x2 << 0)
+#define RT1318_AD_ANA_STO2_DIV8 (0x3 << 0)
+#define RT1318_AD_ANA_STO2_DIV16 (0x4 << 0)
+#define RT1318_AD_ANA_STO2_SFT 0
+/* Clock-5 (0xC006) */
+#define RT1318_DIV_FIFO_IN_MASK (0x3 << 4)
+#define RT1318_DIV_FIFO_IN_SFT 4
+#define RT1318_DIV_FIFO_IN_DIV1 (0x0 << 4)
+#define RT1318_DIV_FIFO_IN_DIV2 (0x1 << 4)
+#define RT1318_DIV_FIFO_IN_DIV4 (0x2 << 4)
+#define RT1318_DIV_FIFO_IN_DIV8 (0x3 << 4)
+#define RT1318_DIV_FIFO_OUT_MASK (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV1 (0x0 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV2 (0x1 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV4 (0x2 << 0)
+#define RT1318_DIV_FIFO_OUT_DIV8 (0x3 << 0)
+#define RT1318_DIV_FIFO_OUT_SFT 0
+/* Clock-6 (0xC007) */
+#define RT1318_DIV_NLMS_MASK (0x3 << 6)
+#define RT1318_DIV_NLMS_SFT 6
+#define RT1318_DIV_NLMS_DIV1 (0x0 << 6)
+#define RT1318_DIV_NLMS_DIV2 (0x1 << 6)
+#define RT1318_DIV_NLMS_DIV4 (0x2 << 6)
+#define RT1318_DIV_NLMS_DIV8 (0x3 << 6)
+#define RT1318_DIV_AD_MONO_MASK (0x7 << 3)
+#define RT1318_DIV_AD_MONO_SFT 3
+#define RT1318_DIV_AD_MONO_DIV1 (0x0 << 3)
+#define RT1318_DIV_AD_MONO_DIV2 (0x1 << 3)
+#define RT1318_DIV_AD_MONO_DIV4 (0x2 << 3)
+#define RT1318_DIV_AD_MONO_DIV8 (0x3 << 3)
+#define RT1318_DIV_AD_MONO_DIV16 (0x4 << 3)
+#define RT1318_DIV_POST_G_MASK (0x7 << 0)
+#define RT1318_DIV_POST_G_SFT 0
+#define RT1318_DIV_POST_G_DIV1 (0x0 << 0)
+#define RT1318_DIV_POST_G_DIV2 (0x1 << 0)
+#define RT1318_DIV_POST_G_DIV4 (0x2 << 0)
+#define RT1318_DIV_POST_G_DIV8 (0x3 << 0)
+#define RT1318_DIV_POST_G_DIV16 (0x4 << 0)
+/* Power Status 1 (0xC121) */
+#define RT1318_PDB_CTRL_MASK (0x1)
+#define RT1318_PDB_CTRL_LOW (0x0)
+#define RT1318_PDB_CTRL_HIGH (0x1)
+#define RT1318_PDB_CTRL_SFT 0
+/* SRC Tcon(0xc204) */
+#define RT1318_SRCIN_IN_SEL_MASK (0x3 << 6)
+#define RT1318_SRCIN_IN_48K (0x0 << 6)
+#define RT1318_SRCIN_IN_44P1 (0x1 << 6)
+#define RT1318_SRCIN_IN_32K (0x2 << 6)
+#define RT1318_SRCIN_IN_16K (0x3 << 6)
+#define RT1318_SRCIN_F12288_MASK (0x3 << 4)
+#define RT1318_SRCIN_TCON1 (0x0 << 4)
+#define RT1318_SRCIN_TCON2 (0x1 << 4)
+#define RT1318_SRCIN_TCON4 (0x2 << 4)
+#define RT1318_SRCIN_TCON8 (0x3 << 4)
+#define RT1318_SRCIN_DACLK_MASK (0x3 << 2)
+#define RT1318_DACLK_TCON1 (0x0 << 2)
+#define RT1318_DACLK_TCON2 (0x1 << 2)
+#define RT1318_DACLK_TCON4 (0x2 << 2)
+#define RT1318_DACLK_TCON8 (0x3 << 2)
+/* R0 Compare Flag (0xDB35) */
+#define RT1318_R0_RANGE_MASK (0x1)
+#define RT1318_R0_OUTOFRANGE (0x0)
+#define RT1318_R0_INRANGE (0x1)
+/* PLL internal setting (0xF20D), K value */
+#define RT1318_K_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF20F), M value */
+#define RT1318_M_PLL1_MASK (0x1f << 0)
+/* PLL internal setting (0xF211), N_8 value */
+#define RT1318_N_8_PLL1_MASK (0x1 << 0)
+/* PLL internal setting (0xF212), N_7_0 value */
+#define RT1318_N_7_0_PLL1_MASK (0xff << 0)
+/* TDM CTRL 1 (0xf900) */
+#define RT1318_TDM_BCLK_MASK (0x1 << 7)
+#define RT1318_TDM_BCLK_NORM (0x0 << 7)
+#define RT1318_TDM_BCLK_INV (0x1 << 7)
+#define RT1318_I2S_FMT_MASK (0x7 << 0)
+#define RT1318_FMT_I2S (0x0 << 0)
+#define RT1318_FMT_LEFT_J (0x1 << 0)
+#define RT1318_FMT_PCM_A_R (0x2 << 0)
+#define RT1318_FMT_PCM_B_R (0x3 << 0)
+#define RT1318_FMT_PCM_A_F (0x6 << 0)
+#define RT1318_FMT_PCM_B_F (0x7 << 0)
+#define RT1318_I2S_FMT_SFT 0
+/* TDM CTRL 2 (0xf901) */
+#define RT1318_I2S_CH_TX_MASK (0x3 << 6)
+#define RT1318_I2S_CH_TX_2CH (0x0 << 6)
+#define RT1318_I2S_CH_TX_4CH (0x1 << 6)
+#define RT1318_I2S_CH_TX_6CH (0x2 << 6)
+#define RT1318_I2S_CH_TX_8CH (0x3 << 6)
+#define RT1318_I2S_CH_RX_MASK (0x3 << 4)
+#define RT1318_I2S_CH_RX_2CH (0x0 << 4)
+#define RT1318_I2S_CH_RX_4CH (0x1 << 4)
+#define RT1318_I2S_CH_RX_6CH (0x2 << 4)
+#define RT1318_I2S_CH_RX_8CH (0x3 << 4)
+#define RT1318_I2S_DL_MASK 0x7
+#define RT1318_I2S_DL_SFT 0
+#define RT1318_I2S_DL_16 0x0
+#define RT1318_I2S_DL_20 0x1
+#define RT1318_I2S_DL_24 0x2
+#define RT1318_I2S_DL_32 0x3
+#define RT1318_I2S_DL_8 0x4
+/* TDM CTRL 3 (0xf902) */
+#define RT1318_I2S_TX_CHL_MASK (0x7 << 4)
+#define RT1318_I2S_TX_CHL_SFT 4
+#define RT1318_I2S_TX_CHL_16 (0x0 << 4)
+#define RT1318_I2S_TX_CHL_20 (0x1 << 4)
+#define RT1318_I2S_TX_CHL_24 (0x2 << 4)
+#define RT1318_I2S_TX_CHL_32 (0x3 << 4)
+#define RT1318_I2S_TX_CHL_8 (0x4 << 4)
+#define RT1318_I2S_RX_CHL_MASK (0x7 << 0)
+#define RT1318_I2S_RX_CHL_SFT 0
+#define RT1318_I2S_RX_CHL_16 (0x0 << 0)
+#define RT1318_I2S_RX_CHL_20 (0x1 << 0)
+#define RT1318_I2S_RX_CHL_24 (0x2 << 0)
+#define RT1318_I2S_RX_CHL_32 (0x3 << 0)
+#define RT1318_I2S_RX_CHL_8 (0x4 << 0)
+/* TDM CTRL 9 (0xf908) */
+#define RT1318_TDM_I2S_TX_L_DAC1_1_MASK (0x7 << 4)
+#define RT1318_TDM_I2S_TX_R_DAC1_1_MASK 0x7
+#define RT1318_TDM_I2S_TX_L_DAC1_1_SFT 4
+#define RT1318_TDM_I2S_TX_R_DAC1_1_SFT 0
+
+#define RT1318_REG_DISP_LEN 23
+
+/* System Clock Source */
+enum {
+ RT1318_SCLK_S_BCLK,
+ RT1318_SCLK_S_SDW,
+ RT1318_SCLK_S_PLL2F,
+ RT1318_SCLK_S_PLL2B,
+ RT1318_SCLK_S_MCLK,
+ RT1318_SCLK_S_RC0,
+ RT1318_SCLK_S_RC1,
+ RT1318_SCLK_S_RC2,
+};
+
+/* PLL Source */
+enum {
+ RT1318_PLL_S_BCLK0,
+ RT1318_PLL_S_BCLK1,
+ RT1318_PLL_S_RC,
+ RT1318_PLL_S_MCLK,
+ RT1318_PLL_S_SDW_IN_PLL,
+ RT1318_PLL_S_SDW_0,
+ RT1318_PLL_S_SDW_1,
+ RT1318_PLL_S_SDW_2,
+};
+
+/* TDM channel */
+enum {
+ RT1318_2CH,
+ RT1318_4CH,
+ RT1318_6CH,
+ RT1318_8CH,
+};
+
+/* R0 calibration result */
+enum {
+ RT1318_R0_OUT_OF_RANGE,
+ RT1318_R0_IN_RANGE,
+ RT1318_R0_CALIB_NOT_DONE,
+};
+
+/* PLL pre-defined M/N/K */
+
+struct pll_calc_map {
+ unsigned int pll_in;
+ unsigned int pll_out;
+ int k;
+ int n;
+ int m;
+ bool m_bp;
+ bool k_bp;
+};
+
+struct rt1318_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;
+};
+
+#endif /* __RT1318_H__ */
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
new file mode 100644
index 000000000000..e3f9b03df3aa
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -0,0 +1,1823 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1320-sdw.c -- rt1320 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/sdw.h>
+#include "rt1320-sdw.h"
+#include "rt-sdw-common.h"
+
+/*
+ * The 'blind writes' is an SDCA term to deal with platform-specific initialization.
+ * It might include vendor-specific or SDCA control registers.
+ */
+static const struct reg_sequence rt1320_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xc01b, 0xfc },
+ { 0xc5c3, 0xf2 },
+ { 0xc5c2, 0x00 },
+ { 0xc5c6, 0x10 },
+ { 0xc5c4, 0x12 },
+ { 0xc5c8, 0x03 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc5d0, 0x0f },
+ { 0xc5d1, 0x89 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xc053, 0x55 },
+ { 0xc052, 0x55 },
+ { 0xc051, 0x13 },
+ { 0xc050, 0x15 },
+ { 0xc060, 0x77 },
+ { 0xc061, 0x55 },
+ { 0xc063, 0x55 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xca05, 0xd6 },
+ { 0xca25, 0xd6 },
+ { 0xcd00, 0x05 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0xff },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x04 },
+ { 0xc900, 0x73 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xc081, 0xfe },
+ { 0xce31, 0x0d },
+ { 0xce30, 0xae },
+ { 0xce37, 0x0b },
+ { 0xce36, 0xd2 },
+ { 0xce39, 0x04 },
+ { 0xce38, 0x80 },
+ { 0xce3f, 0x00 },
+ { 0xce3e, 0x00 },
+ { 0xd470, 0x8b },
+ { 0xd471, 0x18 },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x3f },
+ { 0xd486, 0xc3 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x01 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x02 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x0000d540, 0x01 },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { 0xd478, 0xff },
+};
+
+static const struct reg_sequence rt1320_vc_blind_write[] = {
+ { 0xc003, 0xe0 },
+ { 0xe80a, 0x01 },
+ { 0xc5c3, 0xf3 },
+ { 0xc057, 0x51 },
+ { 0xc054, 0x35 },
+ { 0xca05, 0xd6 },
+ { 0xca07, 0x07 },
+ { 0xca25, 0xd6 },
+ { 0xca27, 0x07 },
+ { 0xc604, 0x40 },
+ { 0xc609, 0x40 },
+ { 0xc046, 0xff },
+ { 0xc045, 0xff },
+ { 0xda81, 0x14 },
+ { 0xda8d, 0x14 },
+ { 0xc044, 0xff },
+ { 0xc043, 0xff },
+ { 0xc042, 0xff },
+ { 0xc041, 0x7f },
+ { 0xc040, 0xff },
+ { 0xcc10, 0x01 },
+ { 0xc700, 0xf0 },
+ { 0xc701, 0x13 },
+ { 0xc901, 0x09 },
+ { 0xc900, 0xd0 },
+ { 0xde03, 0x05 },
+ { 0xdd0b, 0x0d },
+ { 0xdd0a, 0xff },
+ { 0xdd09, 0x0d },
+ { 0xdd08, 0xff },
+ { 0xc570, 0x08 },
+ { 0xc086, 0x02 },
+ { 0xc085, 0x7f },
+ { 0xc084, 0x00 },
+ { 0xc081, 0xfe },
+ { 0xf084, 0x0f },
+ { 0xf083, 0xff },
+ { 0xf082, 0xff },
+ { 0xf081, 0xff },
+ { 0xf080, 0xff },
+ { 0xe802, 0xf8 },
+ { 0xe803, 0xbe },
+ { 0xc003, 0xc0 },
+ { 0xd470, 0xec },
+ { 0xd471, 0x3a },
+ { 0xd474, 0x11 },
+ { 0xd475, 0x32 },
+ { 0xd478, 0xff },
+ { 0xd479, 0x20 },
+ { 0xd47a, 0x10 },
+ { 0xd47c, 0xff },
+ { 0xc019, 0x10 },
+ { 0xd487, 0x0b },
+ { 0xd487, 0x3b },
+ { 0xd486, 0xc3 },
+ { 0xc598, 0x04 },
+ { 0xdb03, 0xf0 },
+ { 0xdb09, 0x00 },
+ { 0xdb08, 0x7a },
+ { 0xdb19, 0x02 },
+ { 0xdb07, 0x5a },
+ { 0xdb05, 0x45 },
+ { 0xd500, 0x00 },
+ { 0xd500, 0x17 },
+ { 0xd600, 0x01 },
+ { 0xd601, 0x02 },
+ { 0xd602, 0x03 },
+ { 0xd603, 0x04 },
+ { 0xd64c, 0x03 },
+ { 0xd64d, 0x03 },
+ { 0xd64e, 0x03 },
+ { 0xd64f, 0x03 },
+ { 0xd650, 0x03 },
+ { 0xd651, 0x03 },
+ { 0xd652, 0x03 },
+ { 0xd610, 0x01 },
+ { 0xd608, 0x03 },
+ { 0xd609, 0x00 },
+ { 0x3fc2bf83, 0x00 },
+ { 0x3fc2bf82, 0x00 },
+ { 0x3fc2bf81, 0x00 },
+ { 0x3fc2bf80, 0x00 },
+ { 0x3fc2bfc7, 0x00 },
+ { 0x3fc2bfc6, 0x00 },
+ { 0x3fc2bfc5, 0x00 },
+ { 0x3fc2bfc4, 0x00 },
+ { 0x3fc2bfc3, 0x00 },
+ { 0x3fc2bfc2, 0x00 },
+ { 0x3fc2bfc1, 0x00 },
+ { 0x3fc2bfc0, 0x03 },
+ { 0x0000d486, 0x43 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00 },
+ { 0x1000db00, 0x07 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x11 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x82 },
+ { 0x1000db06, 0x04 },
+ { 0x1000db07, 0xf1 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x40 },
+ { 0x1000db0b, 0x02 },
+ { 0x1000db0c, 0xf2 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0xe0 },
+ { 0x1000db10, 0x00 },
+ { 0x1000db11, 0x10 },
+ { 0x1000db12, 0x00 },
+ { 0x1000db13, 0x00 },
+ { 0x1000db14, 0x45 },
+ { 0x1000db15, 0x0d },
+ { 0x1000db16, 0x01 },
+ { 0x1000db17, 0x00 },
+ { 0x1000db18, 0x00 },
+ { 0x1000db19, 0xbf },
+ { 0x1000db1a, 0x13 },
+ { 0x1000db1b, 0x09 },
+ { 0x1000db1c, 0x00 },
+ { 0x1000db1d, 0x00 },
+ { 0x1000db1e, 0x00 },
+ { 0x1000db1f, 0x12 },
+ { 0x1000db20, 0x09 },
+ { 0x1000db21, 0x00 },
+ { 0x1000db22, 0x00 },
+ { 0x1000db23, 0x00 },
+ { 0x0000d540, 0x01 },
+ { 0x0000c081, 0xfc },
+ { 0x0000f01e, 0x80 },
+ { 0xc01b, 0xfc },
+ { 0xc5d1, 0x89 },
+ { 0xc5d8, 0x0a },
+ { 0xc5f7, 0x22 },
+ { 0xc5f6, 0x22 },
+ { 0xc065, 0xa5 },
+ { 0xc06b, 0x0a },
+ { 0xd172, 0x2a },
+ { 0xc5d6, 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_sequence rt1321_blind_write[] = {
+ { 0x0000c003, 0xf0 },
+ { 0x0000c01b, 0xfc },
+ { 0x0000c5c3, 0xf2 },
+ { 0x0000c5c2, 0x00 },
+ { 0x0000c5c1, 0x10 },
+ { 0x0000c5c0, 0x04 },
+ { 0x0000c5c7, 0x03 },
+ { 0x0000c5c6, 0x10 },
+ { 0x0000c526, 0x47 },
+ { 0x0000c5c4, 0x12 },
+ { 0x0000c5c5, 0x60 },
+ { 0x0000c520, 0x10 },
+ { 0x0000c521, 0x32 },
+ { 0x0000c5c7, 0x00 },
+ { 0x0000c5c8, 0x03 },
+ { 0x0000c5d3, 0x08 },
+ { 0x0000c5d2, 0x0a },
+ { 0x0000c5d1, 0x49 },
+ { 0x0000c5d0, 0x0f },
+ { 0x0000c580, 0x10 },
+ { 0x0000c581, 0x32 },
+ { 0x0000c582, 0x01 },
+ { 0x0000cb00, 0x03 },
+ { 0x0000cb02, 0x52 },
+ { 0x0000cb04, 0x80 },
+ { 0x0000cb0b, 0x01 },
+ { 0x0000c682, 0x60 },
+ { 0x0000c019, 0x10 },
+ { 0x0000c5f0, 0x01 },
+ { 0x0000c5f7, 0x22 },
+ { 0x0000c5f6, 0x22 },
+ { 0x0000c057, 0x51 },
+ { 0x0000c054, 0x55 },
+ { 0x0000c053, 0x55 },
+ { 0x0000c052, 0x55 },
+ { 0x0000c051, 0x01 },
+ { 0x0000c050, 0x15 },
+ { 0x0000c060, 0x99 },
+ { 0x0000c030, 0x55 },
+ { 0x0000c061, 0x55 },
+ { 0x0000c063, 0x55 },
+ { 0x0000c065, 0xa5 },
+ { 0x0000c06b, 0x0a },
+ { 0x0000ca05, 0xd6 },
+ { 0x0000ca07, 0x07 },
+ { 0x0000ca25, 0xd6 },
+ { 0x0000ca27, 0x07 },
+ { 0x0000cd00, 0x05 },
+ { 0x0000c604, 0x40 },
+ { 0x0000c609, 0x40 },
+ { 0x0000c046, 0xf7 },
+ { 0x0000c045, 0xff },
+ { 0x0000c044, 0xff },
+ { 0x0000c043, 0xff },
+ { 0x0000c042, 0xff },
+ { 0x0000c041, 0xff },
+ { 0x0000c040, 0xff },
+ { 0x0000c049, 0xff },
+ { 0x0000c028, 0x3f },
+ { 0x0000c020, 0x3f },
+ { 0x0000c032, 0x13 },
+ { 0x0000c033, 0x01 },
+ { 0x0000cc10, 0x01 },
+ { 0x0000dc20, 0x03 },
+ { 0x0000de03, 0x05 },
+ { 0x0000dc00, 0x00 },
+ { 0x0000c700, 0xf0 },
+ { 0x0000c701, 0x13 },
+ { 0x0000c900, 0xc3 },
+ { 0x0000c570, 0x08 },
+ { 0x0000c086, 0x02 },
+ { 0x0000c085, 0x7f },
+ { 0x0000c084, 0x00 },
+ { 0x0000c081, 0xff },
+ { 0x0000f084, 0x0f },
+ { 0x0000f083, 0xff },
+ { 0x0000f082, 0xff },
+ { 0x0000f081, 0xff },
+ { 0x0000f080, 0xff },
+ { 0x20003003, 0x3f },
+ { 0x20005818, 0x81 },
+ { 0x20009018, 0x81 },
+ { 0x2000301c, 0x81 },
+ { 0x0000c003, 0xc0 },
+ { 0x0000c047, 0x80 },
+ { 0x0000d541, 0x80 },
+ { 0x0000d487, 0x0b },
+ { 0x0000d487, 0x3b },
+ { 0x0000d486, 0xc3 },
+ { 0x0000d470, 0x89 },
+ { 0x0000d471, 0x3a },
+ { 0x0000d472, 0x1d },
+ { 0x0000d478, 0xff },
+ { 0x0000d479, 0x20 },
+ { 0x0000d47a, 0x10 },
+ { 0x0000d73c, 0xb7 },
+ { 0x0000d73d, 0xd7 },
+ { 0x0000d73e, 0x00 },
+ { 0x0000d73f, 0x10 },
+ { 0x3fc2dfc3, 0x00 },
+ { 0x3fc2dfc2, 0x00 },
+ { 0x3fc2dfc1, 0x00 },
+ { 0x3fc2dfc0, 0x07 },
+ { 0x3fc2dfc7, 0x00 },
+ { 0x3fc2dfc6, 0x00 },
+ { 0x3fc2dfc5, 0x00 },
+ { 0x3fc2dfc4, 0x01 },
+ { 0x3fc2df83, 0x00 },
+ { 0x3fc2df82, 0x00 },
+ { 0x3fc2df81, 0x00 },
+ { 0x3fc2df80, 0x00 },
+ { 0x0000d541, 0x40 },
+ { 0x0000d486, 0x43 },
+ { 0x1000db00, 0x03 },
+ { 0x1000db01, 0x00 },
+ { 0x1000db02, 0x10 },
+ { 0x1000db03, 0x00 },
+ { 0x1000db04, 0x00 },
+ { 0x1000db05, 0x45 },
+ { 0x1000db06, 0x12 },
+ { 0x1000db07, 0x09 },
+ { 0x1000db08, 0x00 },
+ { 0x1000db09, 0x00 },
+ { 0x1000db0a, 0x00 },
+ { 0x1000db0b, 0x13 },
+ { 0x1000db0c, 0x09 },
+ { 0x1000db0d, 0x00 },
+ { 0x1000db0e, 0x00 },
+ { 0x1000db0f, 0x00 },
+ { 0x0000d540, 0x21 },
+ { 0x41000189, 0x00 },
+ { 0x4100018a, 0x00 },
+ { 0x41001988, 0x00 },
+ { 0x41081400, 0x09 },
+ { 0x40801508, 0x03 },
+ { 0x40801588, 0x03 },
+ { 0x40801809, 0x00 },
+ { 0x4080180a, 0x00 },
+ { 0x4080180b, 0x00 },
+ { 0x4080180c, 0x00 },
+ { 0x40801b09, 0x00 },
+ { 0x40801b0a, 0x00 },
+ { 0x40801b0b, 0x00 },
+ { 0x40801b0c, 0x00 },
+ { 0x0000d714, 0x17 },
+ { 0x20009012, 0x00 },
+ { 0x0000dd0b, 0x0d },
+ { 0x0000dd0a, 0xff },
+ { 0x0000dd09, 0x0d },
+ { 0x0000dd08, 0xff },
+ { 0x0000d172, 0x2a },
+ { 0x41001988, 0x03 },
+};
+
+static const struct reg_default rt1320_reg_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x0b },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), 0x03 },
+};
+
+static const struct reg_default rt1320_mbq_defaults[] = {
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+};
+
+static bool rt1320_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000 ... 0xc086:
+ case 0xc400 ... 0xc409:
+ case 0xc480 ... 0xc48f:
+ case 0xc4c0 ... 0xc4c4:
+ case 0xc4e0 ... 0xc4e7:
+ case 0xc500:
+ case 0xc560 ... 0xc56b:
+ case 0xc570:
+ case 0xc580 ... 0xc59a:
+ case 0xc5b0 ... 0xc60f:
+ case 0xc640 ... 0xc64f:
+ case 0xc670:
+ case 0xc680 ... 0xc683:
+ case 0xc700 ... 0xc76f:
+ case 0xc800 ... 0xc801:
+ case 0xc820:
+ case 0xc900 ... 0xc901:
+ case 0xc920 ... 0xc921:
+ case 0xca00 ... 0xca07:
+ case 0xca20 ... 0xca27:
+ case 0xca40 ... 0xca4b:
+ case 0xca60 ... 0xca68:
+ case 0xca80 ... 0xca88:
+ case 0xcb00 ... 0xcb0c:
+ case 0xcc00 ... 0xcc12:
+ case 0xcc80 ... 0xcc81:
+ case 0xcd00:
+ case 0xcd80 ... 0xcd82:
+ case 0xce00 ... 0xce4d:
+ case 0xcf00 ... 0xcf25:
+ case 0xd000 ... 0xd0ff:
+ case 0xd100 ... 0xd1ff:
+ case 0xd200 ... 0xd2ff:
+ case 0xd300 ... 0xd3ff:
+ case 0xd400 ... 0xd403:
+ case 0xd410 ... 0xd417:
+ case 0xd470 ... 0xd497:
+ case 0xd4dc ... 0xd50f:
+ case 0xd520 ... 0xd543:
+ case 0xd560 ... 0xd5ef:
+ case 0xd600 ... 0xd663:
+ case 0xda00 ... 0xda6e:
+ case 0xda80 ... 0xda9e:
+ case 0xdb00 ... 0xdb7f:
+ case 0xdc00:
+ case 0xdc20 ... 0xdc21:
+ case 0xdd00 ... 0xdd17:
+ case 0xde00 ... 0xde09:
+ case 0xdf00 ... 0xdf1b:
+ case 0xe000 ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x1000cd91 ... 0x1000cd96:
+ case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc29d80 ... 0x3fc29d83:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ /* 0x40801508/0x40801809/0x4080180a/0x40801909/0x4080190a */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x40880900/0x40880980 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x40881500 */
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ /* 0x41000189/0x4100018a */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02):
+ /* 0x41001388 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE27, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41001988 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0):
+ /* 0x41080000 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ /* 0x41080200 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0):
+ /* 0x41080900 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41080980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081080 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ /* 0x41081480/0x41081488 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ /* 0x41081980 */
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0xc000:
+ case 0xc003:
+ case 0xc081:
+ case 0xc402 ... 0xc406:
+ case 0xc48c ... 0xc48f:
+ case 0xc560:
+ case 0xc5b5 ... 0xc5b7:
+ case 0xc5fc ... 0xc5ff:
+ case 0xc680 ... 0xc683:
+ case 0xc820:
+ case 0xc900:
+ case 0xc920:
+ case 0xca42:
+ case 0xca62:
+ case 0xca82:
+ case 0xcd00:
+ case 0xce03:
+ case 0xce10:
+ case 0xce14 ... 0xce17:
+ case 0xce44 ... 0xce49:
+ case 0xce4c ... 0xce4d:
+ case 0xcf0c:
+ case 0xcf10 ... 0xcf25:
+ case 0xd486 ... 0xd487:
+ case 0xd4e5 ... 0xd4e6:
+ case 0xd4e8 ... 0xd4ff:
+ case 0xd530:
+ case 0xd540 ... 0xd541:
+ case 0xd543:
+ case 0xdb58 ... 0xdb5f:
+ case 0xdb60 ... 0xdb63:
+ case 0xdb68 ... 0xdb69:
+ case 0xdb6d:
+ case 0xdb70 ... 0xdb71:
+ case 0xdb76:
+ case 0xdb7a:
+ case 0xdb7c ... 0xdb7f:
+ case 0xdd0c ... 0xdd13:
+ case 0xde02:
+ case 0xdf14 ... 0xdf1b:
+ case 0xe83c ... 0xe847:
+ case 0xf01e:
+ case 0xf717 ... 0xf719:
+ case 0xf720 ... 0xf723:
+ case 0x10000000 ... 0x10008fff:
+ case 0x1000c000 ... 0x1000dfff:
+ case 0x1000f008:
+ case 0x1000f021:
+ case 0x2000300f:
+ case 0x2000301c:
+ case 0x2000900f:
+ case 0x20009018:
+ case 0x3fc2ab80 ... 0x3fc2abd4:
+ case 0x3fc2b780:
+ case 0x3fc2bf80 ... 0x3fc2bf83:
+ case 0x3fc2bfc0 ... 0x3fc2bfc8:
+ case 0x3fc2d300 ... 0x3fc2d354:
+ case 0x3fc2dfc0 ... 0x3fc2dfc8:
+ case 0x3fe2e000 ... 0x3fe2e003:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_SAPU, RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1320_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt1320_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1320_readable_register,
+ .volatile_reg = rt1320_volatile_register,
+ .max_register = 0x41081980,
+ .reg_defaults = rt1320_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt1320_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt1320_mbq_readable_register,
+ .max_register = 0x41000192,
+ .reg_defaults = rt1320_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1320_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1320_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;
+
+ /*
+ * Due to support the multi-lane, we call 'sdw_slave_read_prop' to get the lane mapping
+ */
+ sdw_slave_read_prop(slave);
+
+ 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;
+ prop->lane_control_support = true;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(4) | BIT(8) | BIT(10);
+ 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 = 64;
+
+ /* BIOS may set wake_capable. Make sure it is 0 as wake events are disabled. */
+ prop->wake_capable = 0;
+
+ return 0;
+}
+
+static int rt1320_pde_transition_delay(struct rt1320_sdw_priv *rt1320, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 2000, val;
+
+ pm_runtime_mark_last_busy(&rt1320->sdw_slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(func, entity, RT1320_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt1320->sdw_slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+/*
+ * The 'patch code' is written to the patch code area.
+ * The patch code area is used for SDCA register expansion flexibility.
+ */
+static void rt1320_load_mcu_patch(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ const struct firmware *patch;
+ const char *filename;
+ unsigned int addr, val, min_addr, max_addr;
+ const unsigned char *ptr;
+ int ret, i;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id <= RT1320_VB)
+ filename = RT1320_VAB_MCU_PATCH;
+ else
+ filename = RT1320_VC_MCU_PATCH;
+ min_addr = 0x10007000;
+ max_addr = 0x10007fff;
+ break;
+ case RT1321_DEV_ID:
+ filename = RT1321_VA_MCU_PATCH;
+ min_addr = 0x10008000;
+ max_addr = 0x10008fff;
+ break;
+ default:
+ dev_err(&slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return;
+ }
+
+ /* load the patch code here */
+ ret = request_firmware(&patch, filename, &slave->dev);
+ if (ret) {
+ dev_err(&slave->dev, "%s: Failed to load %s firmware", __func__, filename);
+ regmap_write(rt1320->regmap, 0xc598, 0x00);
+ regmap_write(rt1320->regmap, min_addr, 0x67);
+ regmap_write(rt1320->regmap, min_addr + 0x1, 0x80);
+ regmap_write(rt1320->regmap, min_addr + 0x2, 0x00);
+ regmap_write(rt1320->regmap, min_addr + 0x3, 0x00);
+ if (rt1320->dev_id == RT1321_DEV_ID) {
+ regmap_write(rt1320->regmap, 0xd73c, 0x67);
+ regmap_write(rt1320->regmap, 0xd73d, 0x80);
+ regmap_write(rt1320->regmap, 0xd73e, 0x00);
+ regmap_write(rt1320->regmap, 0xd73f, 0x00);
+ }
+ } else {
+ ptr = (const unsigned char *)patch->data;
+ if ((patch->size % 8) == 0) {
+ for (i = 0; i < patch->size; i += 8) {
+ addr = (ptr[i] & 0xff) | (ptr[i + 1] & 0xff) << 8 |
+ (ptr[i + 2] & 0xff) << 16 | (ptr[i + 3] & 0xff) << 24;
+ val = (ptr[i + 4] & 0xff) | (ptr[i + 5] & 0xff) << 8 |
+ (ptr[i + 6] & 0xff) << 16 | (ptr[i + 7] & 0xff) << 24;
+
+ if (addr > max_addr || addr < min_addr) {
+ dev_err(&slave->dev, "%s: the address 0x%x is wrong", __func__, addr);
+ goto _exit_;
+ }
+ if (val > 0xff) {
+ dev_err(&slave->dev, "%s: the value 0x%x is wrong", __func__, val);
+ goto _exit_;
+ }
+ regmap_write(rt1320->regmap, addr, val);
+ }
+ }
+_exit_:
+ release_firmware(patch);
+ }
+}
+
+static void rt1320_vab_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_blind_write); i++) {
+ reg = rt1320_blind_write[i].reg;
+ val = rt1320_blind_write[i].def;
+ delay = rt1320_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bfc7)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+ }
+}
+
+static void rt1320_vc_preset(struct rt1320_sdw_priv *rt1320)
+{
+ struct sdw_slave *slave = rt1320->sdw_slave;
+ unsigned int i, reg, val, delay, retry, tmp;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320_vc_blind_write); i++) {
+ reg = rt1320_vc_blind_write[i].reg;
+ val = rt1320_vc_blind_write[i].def;
+ delay = rt1320_vc_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2bf83)
+ rt1320_load_mcu_patch(rt1320);
+
+ if ((reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0)) &&
+ (val == 0x00)) {
+ retry = 200;
+ while (retry) {
+ regmap_read(rt1320->regmap, RT1320_KR0_INT_READY, &tmp);
+ dev_dbg(&slave->dev, "%s, RT1320_KR0_INT_READY=0x%x\n", __func__, tmp);
+ if (tmp == 0x1f)
+ break;
+ usleep_range(1000, 1500);
+ retry--;
+ }
+ if (!retry)
+ dev_warn(&slave->dev, "%s MCU is NOT ready!", __func__);
+ }
+ regmap_write(rt1320->regmap, reg, val);
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static void rt1321_preset(struct rt1320_sdw_priv *rt1320)
+{
+ unsigned int i, reg, val, delay;
+
+ for (i = 0; i < ARRAY_SIZE(rt1321_blind_write); i++) {
+ reg = rt1321_blind_write[i].reg;
+ val = rt1321_blind_write[i].def;
+ delay = rt1321_blind_write[i].delay_us;
+
+ if (reg == 0x3fc2dfc3)
+ rt1320_load_mcu_patch(rt1320);
+
+ regmap_write(rt1320->regmap, reg, val);
+
+ if (delay)
+ usleep_range(delay, delay + 1000);
+
+ if (reg == SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0))
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, val);
+ }
+}
+
+static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned int amp_func_status, val, tmp;
+
+ if (rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, true);
+ regcache_cache_bypass(rt1320->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);
+
+ if (rt1320->version_id < 0) {
+ regmap_read(rt1320->regmap, RT1320_DEV_VERSION_ID_1, &val);
+ rt1320->version_id = val;
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_0, &val);
+ regmap_read(rt1320->regmap, RT1320_DEV_ID_1, &tmp);
+ rt1320->dev_id = (val << 8) | tmp;
+ }
+
+ regmap_read(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ /* initialization write */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (rt1320->version_id < RT1320_VC)
+ rt1320_vab_preset(rt1320);
+ else
+ rt1320_vc_preset(rt1320);
+ break;
+ case RT1321_DEV_ID:
+ rt1321_preset(rt1320);
+ break;
+ default:
+ dev_err(dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ }
+
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT0, RT1320_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+ if (!rt1320->first_hw_init && rt1320->version_id == RT1320_VA && rt1320->dev_id == RT1320_DEV_ID) {
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_0, &val);
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_1, &tmp);
+ val = (tmp << 8) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_2, &tmp);
+ val = (tmp << 16) | val;
+ regmap_read(rt1320->regmap, RT1320_HIFI_VER_3, &tmp);
+ val = (tmp << 24) | val;
+ dev_dbg(dev, "%s ROM version=0x%x\n", __func__, val);
+ /*
+ * We call the version b which has the new DSP ROM code against version a.
+ * Therefore, we read the DSP address to check the ID.
+ */
+ if (val == RT1320_VER_B_ID)
+ rt1320->version_id = RT1320_VB;
+ regmap_write(rt1320->regmap, SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 3);
+ }
+ dev_dbg(dev, "%s version_id=%d, dev_id=0x%x\n", __func__, rt1320->version_id, rt1320->dev_id);
+
+ if (rt1320->first_hw_init) {
+ regcache_cache_bypass(rt1320->regmap, false);
+ regcache_cache_bypass(rt1320->mbq_regmap, false);
+ regcache_mark_dirty(rt1320->regmap);
+ regcache_mark_dirty(rt1320->mbq_regmap);
+ }
+
+ /* Mark Slave initialization complete */
+ rt1320->first_hw_init = true;
+ rt1320->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+static int rt1320_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1320->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1320->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1320_io_init(&slave->dev, slave);
+}
+
+static int rt1320_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_MIC, RT1320_SDCA_ENT_PDE11, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23,
+ RT1320_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt1320_pde_transition_delay(rt1320, FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, ps3);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1320_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ unsigned int gain_l_val, gain_r_val;
+ unsigned int lvalue, rvalue;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int changed = 0, reg_base;
+ struct rt_sdca_dmic_kctrl_priv *p;
+ unsigned int regvalue[4], gain_val[4], i;
+ int err;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt1320->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;
+ 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;
+ 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(rt1320->mbq_regmap, mc->reg, gain_l_val);
+ /* Rch */
+ regmap_write(rt1320->mbq_regmap, mc->rreg, gain_r_val);
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue[i]);
+ break;
+ }
+
+ gain_val[i] = ucontrol->value.integer.value[i];
+ if (gain_val[i] > p->max)
+ gain_val[i] = p->max;
+
+ 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++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i - 2, gain_val[i]);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ err = regmap_write(rt1320->mbq_regmap, reg_base + i, gain_val[i]);
+ break;
+ }
+
+ if (err < 0)
+ dev_err(&rt1320->sdw_slave->dev, "0x%08x can't be set\n", reg_base + i);
+ }
+
+_done_:
+ return 1;
+}
+
+static int rt1320_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = 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;
+ const unsigned int interval_offset = 0xc0;
+ unsigned int reg_base, regvalue, ctl, i;
+ struct rt_sdca_dmic_kctrl_priv *p;
+
+ if (strstr(ucontrol->id.name, "FU Capture Volume"))
+ goto _dmic_vol_;
+
+ regmap_read(rt1320->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt1320->mbq_regmap, mc->rreg, &read_r);
+
+ ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+
+ if (read_l != read_r)
+ 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;
+ goto _done_;
+
+_dmic_vol_:
+ p = (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+ /* check all channels */
+ for (i = 0; i < p->count; i++) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2) {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ } else {
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i - 2, &regvalue);
+ }
+ break;
+ case RT1321_DEV_ID:
+ reg_base = SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01);
+ regmap_read(rt1320->mbq_regmap, reg_base + i, &regvalue);
+ break;
+ }
+
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+ ucontrol->value.integer.value[i] = ctl;
+ }
+_done_:
+ return 0;
+}
+
+static int rt1320_set_fu_capture_ctl(struct rt1320_sdw_priv *rt1320)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt1320->fu_mixer_mute); i++) {
+ ch_mute = (rt1320->fu_dapm_mute || rt1320->fu_mixer_mute[i]) ? 0x01 : 0x00;
+
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ if (i < 2)
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ else
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU14,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i - 2, ch_mute);
+ break;
+ case RT1321_DEV_ID:
+ err = regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113,
+ RT1320_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ break;
+ default:
+ dev_err(&rt1320->sdw_slave->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt1320->fu_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt1320_dmic_fu_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt1320->fu_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt1320->fu_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt1320_set_fu_capture_ctl(rt1320);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt1320_dmic_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt_sdca_dmic_kctrl_priv *p =
+ (struct rt_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 rt1320_dmic_fu_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 rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt1320->fu_dapm_mute = false;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt1320->fu_dapm_mute = true;
+ rt1320_set_fu_capture_ctl(rt1320);
+ break;
+ }
+ return 0;
+}
+
+static const char * const rt1320_rx_data_ch_select[] = {
+ "L,R",
+ "R,L",
+ "L,L",
+ "R,R",
+ "L,L+R",
+ "R,L+R",
+ "L+R,L",
+ "L+R,R",
+ "L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1320_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PPU21, RT1320_SDCA_CTL_POSTURE_NUMBER, 0), 0,
+ rt1320_rx_data_ch_select);
+
+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 struct snd_kcontrol_new rt1320_snd_controls[] = {
+ SOC_DOUBLE_R_EXT_TLV("FU21 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_VOLUME, CH_02),
+ 0, 0x57, 0, rt1320_set_gain_get, rt1320_set_gain_put, out_vol_tlv),
+ SOC_ENUM("RX Channel Select", rt1320_rx_data_ch_enum),
+
+ RT_SDCA_FU_CTRL("FU Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4, rt1320_dmic_fu_info, rt1320_dmic_fu_capture_get, rt1320_dmic_fu_capture_put),
+ RT_SDCA_EXT_TLV("FU Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_FU113, RT1320_SDCA_CTL_FU_VOLUME, CH_01),
+ rt1320_set_gain_get, rt1320_set_gain_put, 4, 0x3f, in_vol_tlv, rt1320_dmic_fu_info),
+};
+
+static const struct snd_kcontrol_new rt1320_spk_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt1320_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_FU21, RT1320_SDCA_CTL_FU_MUTE, CH_02),
+ 0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1320_dapm_widgets[] = {
+ /* Audio Interface */
+ 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),
+ SND_SOC_DAPM_AIF_OUT("DP8-10TX", "DP8-10 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("FU21", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt1320_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt1320_pde11_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC("FU 113", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("FU 14", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA_E("FU", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1320_dmic_fu_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt1320_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt1320_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+
+ /* Input */
+ SND_SOC_DAPM_PGA("AEC Data", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("AEC Gen"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+};
+
+static const struct snd_soc_dapm_route rt1320_dapm_routes[] = {
+ { "FU21", NULL, "DP1RX" },
+ { "FU21", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU21" },
+ { "OT23 R", "Switch", "FU21" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+
+ { "AEC Data", NULL, "AEC Gen" },
+ { "DP4TX", NULL, "AEC Data" },
+
+ {"DP8-10TX", NULL, "FU"},
+ {"FU", NULL, "PDE 11"},
+ {"FU", NULL, "FU 113"},
+ {"FU", NULL, "FU 14"},
+ {"FU 113", NULL, "DMIC1"},
+ {"FU 14", NULL, "DMIC2"},
+};
+
+static int rt1320_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 rt1320_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1320_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 rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_port_config dmic_port_config[2];
+ 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 (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 1;
+ else
+ return -EINVAL;
+ } else {
+ if (dai->id == RT1320_AIF1)
+ port_config.num = 4;
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ dmic_port_config[1].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[1].num = 10;
+ break;
+ case RT1321_DEV_ID:
+ dmic_port_config[0].ch_mask = BIT(0) | BIT(1);
+ dmic_port_config[0].num = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ }
+
+ if (dai->id == RT1320_AIF1)
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ else if (dai->id == RT1320_AIF2) {
+ switch (rt1320->dev_id) {
+ case RT1320_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 2, sdw_stream);
+ break;
+ case RT1321_DEV_ID:
+ retval = sdw_stream_add_slave(rt1320->sdw_slave, &stream_config,
+ dmic_port_config, 1, sdw_stream);
+ break;
+ default:
+ dev_err(dai->dev, "%s: Unknown device ID %d\n", __func__, rt1320->dev_id);
+ return -EINVAL;
+ }
+ } else
+ return -EINVAL;
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT1320_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT1320_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT1320_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1320_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1320_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1320_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT1320_AIF1)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_CS21, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ else {
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS113, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ if (rt1320->dev_id == RT1320_DEV_ID)
+ regmap_write(rt1320->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, RT1320_SDCA_ENT_CS14, RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ }
+
+ return 0;
+}
+
+static int rt1320_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1320_sdw_priv *rt1320 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1320->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1320->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 rt1320_slave_ops = {
+ .read_prop = rt1320_read_prop,
+ .update_status = rt1320_update_status,
+};
+
+static int rt1320_sdw_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct rt1320_sdw_priv *rt1320 = snd_soc_component_get_drvdata(component);
+
+ rt1320->component = component;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ dev_dbg(&rt1320->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_rt1320 = {
+ .probe = rt1320_sdw_component_probe,
+ .controls = rt1320_snd_controls,
+ .num_controls = ARRAY_SIZE(rt1320_snd_controls),
+ .dapm_widgets = rt1320_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1320_dapm_widgets),
+ .dapm_routes = rt1320_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1320_dapm_routes),
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt1320_aif_dai_ops = {
+ .hw_params = rt1320_sdw_hw_params,
+ .hw_free = rt1320_sdw_pcm_hw_free,
+ .set_stream = rt1320_set_sdw_stream,
+ .shutdown = rt1320_sdw_shutdown,
+};
+
+#define RT1320_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 RT1320_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rt1320_sdw_dai[] = {
+ {
+ .name = "rt1320-aif1",
+ .id = RT1320_AIF1,
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+ /* DMIC: DP8 2ch + DP10 2ch */
+ {
+ .name = "rt1320-aif2",
+ .id = RT1320_AIF2,
+ .capture = {
+ .stream_name = "DP8-10 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT1320_STEREO_RATES,
+ .formats = RT1320_FORMATS,
+ },
+ .ops = &rt1320_aif_dai_ops,
+ },
+};
+
+static int rt1320_sdw_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt1320_sdw_priv *rt1320;
+ int ret;
+
+ rt1320 = devm_kzalloc(dev, sizeof(*rt1320), GFP_KERNEL);
+ if (!rt1320)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1320);
+ rt1320->sdw_slave = slave;
+ rt1320->mbq_regmap = mbq_regmap;
+ rt1320->regmap = regmap;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1320->hw_init = false;
+ rt1320->first_hw_init = false;
+ rt1320->version_id = -1;
+ rt1320->fu_dapm_mute = true;
+ rt1320->fu_mixer_mute[0] = rt1320->fu_mixer_mute[1] =
+ rt1320->fu_mixer_mute[2] = rt1320->fu_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_component_sdw_rt1320,
+ rt1320_sdw_dai,
+ ARRAY_SIZE(rt1320_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 rt1320_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, &rt1320_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt1320_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt1320_sdw_remove(struct sdw_slave *slave)
+{
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+/*
+ * Version A/B will use the class id 0
+ * The newer version than A/B will use the class id 1, so add it in advance
+ */
+static const struct sdw_device_id rt1320_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1320, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1321, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1320_id);
+
+static int rt1320_dev_suspend(struct device *dev)
+{
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+
+ if (!rt1320->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1320->regmap, true);
+ regcache_cache_only(rt1320->mbq_regmap, true);
+ return 0;
+}
+
+#define RT1320_PROBE_TIMEOUT 5000
+
+static int rt1320_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1320->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1320_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1320->regmap, false);
+ regcache_sync(rt1320->regmap);
+ regcache_cache_only(rt1320->mbq_regmap, false);
+ regcache_sync(rt1320->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt1320_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume)
+ RUNTIME_PM_OPS(rt1320_dev_suspend, rt1320_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1320_sdw_driver = {
+ .driver = {
+ .name = "rt1320-sdca",
+ .pm = pm_ptr(&rt1320_pm),
+ },
+ .probe = rt1320_sdw_probe,
+ .remove = rt1320_sdw_remove,
+ .ops = &rt1320_slave_ops,
+ .id_table = rt1320_id,
+};
+module_sdw_driver(rt1320_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1320 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1320-sdw.h b/sound/soc/codecs/rt1320-sdw.h
new file mode 100644
index 000000000000..a6d90e259dc9
--- /dev/null
+++ b/sound/soc/codecs/rt1320-sdw.h
@@ -0,0 +1,113 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1320-sdw.h -- RT1320 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1320_SDW_H__
+#define __RT1320_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+#define RT1320_DEV_ID 0x6981
+#define RT1321_DEV_ID 0x7045
+
+/* imp-defined registers */
+#define RT1320_DEV_VERSION_ID_1 0xc404
+#define RT1320_DEV_ID_1 0xc405
+#define RT1320_DEV_ID_0 0xc406
+
+#define RT1321_PATCH_MAIN_VER 0x1000cffe
+#define RT1321_PATCH_BETA_VER 0x1000cfff
+
+#define RT1320_KR0_STATUS_CNT 0x1000f008
+#define RT1320_KR0_INT_READY 0x1000f021
+#define RT1320_HIFI_VER_0 0x3fe2e000
+#define RT1320_HIFI_VER_1 0x3fe2e001
+#define RT1320_HIFI_VER_2 0x3fe2e002
+#define RT1320_HIFI_VER_3 0x3fe2e003
+
+/* RT1320 SDCA Control - function number */
+#define FUNC_NUM_AMP 0x04
+#define FUNC_NUM_MIC 0x02
+
+/* RT1320 SDCA entity */
+#define RT1320_SDCA_ENT0 0x00
+#define RT1320_SDCA_ENT_PDE11 0x2a
+#define RT1320_SDCA_ENT_PDE23 0x33
+#define RT1320_SDCA_ENT_PDE27 0x27
+#define RT1320_SDCA_ENT_FU14 0x32
+#define RT1320_SDCA_ENT_FU21 0x03
+#define RT1320_SDCA_ENT_FU113 0x30
+#define RT1320_SDCA_ENT_CS14 0x13
+#define RT1320_SDCA_ENT_CS21 0x21
+#define RT1320_SDCA_ENT_CS113 0x12
+#define RT1320_SDCA_ENT_SAPU 0x29
+#define RT1320_SDCA_ENT_PPU21 0x04
+
+/* RT1320 SDCA control */
+#define RT1320_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1320_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1320_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+#define RT1320_SDCA_CTL_FU_MUTE 0x01
+#define RT1320_SDCA_CTL_FU_VOLUME 0x02
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_MODE 0x10
+#define RT1320_SDCA_CTL_SAPU_PROTECTION_STATUS 0x11
+#define RT1320_SDCA_CTL_POSTURE_NUMBER 0x10
+#define RT1320_SDCA_CTL_FUNC_STATUS 0x10
+
+/* RT1320 SDCA channel */
+#define CH_01 0x01
+#define CH_02 0x02
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+/* Sample Frequency Index */
+#define RT1320_SDCA_RATE_16000HZ 0x04
+#define RT1320_SDCA_RATE_32000HZ 0x07
+#define RT1320_SDCA_RATE_44100HZ 0x08
+#define RT1320_SDCA_RATE_48000HZ 0x09
+#define RT1320_SDCA_RATE_96000HZ 0x0b
+#define RT1320_SDCA_RATE_192000HZ 0x0d
+
+enum {
+ RT1320_AIF1,
+ RT1320_AIF2,
+};
+
+/*
+ * The version id will be useful to distinguish the capability between the different IC versions.
+ * Currently, VA and VB have different DSP FW versions.
+ */
+enum rt1320_version_id {
+ RT1320_VA,
+ RT1320_VB,
+ RT1320_VC,
+};
+
+#define RT1320_VER_B_ID 0x07392238
+#define RT1320_VAB_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vab.bin"
+#define RT1320_VC_MCU_PATCH "realtek/rt1320/rt1320-patch-code-vc.bin"
+#define RT1321_VA_MCU_PATCH "realtek/rt1320/rt1321-patch-code-va.bin"
+
+struct rt1320_sdw_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+ int version_id;
+ unsigned int dev_id;
+ bool fu_dapm_mute;
+ bool fu_mixer_mute[4];
+};
+
+#endif /* __RT1320_SDW_H__ */
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
new file mode 100644
index 000000000000..2c055c45111f
--- /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 <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+
+#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_CBP_CFP:
+ 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_CBC_CFC:
+ 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"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt274_acpi_match[] = {
+ { "10EC0274" },
+ { "INT34C2" },
+ { }
+};
+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 request 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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt274.h b/sound/soc/codecs/rt274.h
new file mode 100644
index 000000000000..0fcf942fa183
--- /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 <bardliao@realtek.com>
+ */
+
+#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
index f374840a5a7c..fd8de8b49793 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt286.c -- RT286 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -29,26 +26,27 @@
#include <sound/jack.h>
#include <linux/workqueue.h>
#include <sound/rt286.h>
-#include <sound/hda_verbs.h>
+#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_codec *codec;
+ 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;
- struct reg_default *index_cache;
};
-static struct reg_default rt286_index_def[] = {
+static const struct reg_default rt286_index_def[] = {
{ 0x01, 0xaaaa },
{ 0x02, 0x8aaa },
{ 0x03, 0x0002 },
@@ -173,6 +171,9 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
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):
@@ -185,102 +186,14 @@ static bool rt286_readable_register(struct device *dev, unsigned int reg)
}
}
-static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
-{
- struct i2c_client *client = context;
- struct rt286_priv *rt286 = i2c_get_clientdata(client);
- u8 data[4];
- int ret, i;
-
- /* handle index registers */
- if (reg <= 0xff) {
- rt286_hw_write(client, RT286_COEF_INDEX, reg);
- for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- if (reg == rt286->index_cache[i].reg) {
- rt286->index_cache[i].def = value;
- break;
- }
-
- }
- reg = RT286_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
- pr_err("ret=%d\n", ret);
- if (ret < 0)
- return ret;
- else
- return -EIO;
-}
-
-static int rt286_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;
- unsigned int index, vid, buf = 0x0;
-
- /* handle index registers */
- if (reg <= 0xff) {
- rt286_hw_write(client, RT286_COEF_INDEX, reg);
- reg = RT286_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;
-}
-
#ifdef CONFIG_PM
-static void rt286_index_sync(struct snd_soc_codec *codec)
+static void rt286_index_sync(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
- snd_soc_write(codec, rt286->index_cache[i].reg,
+ snd_soc_component_write(component, rt286->index_cache[i].reg,
rt286->index_cache[i].def);
}
}
@@ -301,13 +214,17 @@ static int 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->codec)
+ 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;
@@ -316,14 +233,11 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
regmap_update_bits(rt286->regmap,
RT286_DC_GAIN, 0x200, 0x200);
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "HV");
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "VREF");
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
/* power LDO1 */
- snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
- "LDO1");
- snd_soc_dapm_sync(&rt286->codec->dapm);
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ snd_soc_dapm_sync(dapm);
regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
msleep(50);
@@ -341,17 +255,24 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
msleep(300);
regmap_read(rt286->regmap,
RT286_CBJ_CTRL2, &val);
- if (0x0070 == (val & 0x0070))
+ if (0x0070 == (val & 0x0070)) {
*mic = true;
- else
+ } 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);
@@ -360,11 +281,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*mic = buf & 0x80000000;
}
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
- if (!*hp)
- snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
- snd_soc_dapm_sync(&rt286->codec->dapm);
+ 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;
}
@@ -379,35 +301,47 @@ static void rt286_jack_detect_work(struct work_struct *work)
rt286_jack_detect(rt286, &hp, &mic);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+static int rt286_mic_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
- /* Send an initial empty report */
- snd_soc_jack_report(rt286->jack, 0,
- SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ 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;
}
-EXPORT_SYMBOL_GPL(rt286_mic_detect);
static int is_mclk_mode(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -506,15 +440,15 @@ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
break;
@@ -528,32 +462,14 @@ static int rt286_spk_event(struct snd_soc_dapm_widget *w,
static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
-static int rt286_vref_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
- mdelay(50);
+ snd_soc_component_write(component, RT286_SET_PIN_DMIC1, 0);
break;
default:
return 0;
@@ -565,14 +481,14 @@ static int rt286_vref_event(struct snd_soc_dapm_widget *w,
static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x08);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+ snd_soc_component_update_bits(component, RT286_POWER_CTRL2, 0x38, 0x30);
break;
default:
return 0;
@@ -584,19 +500,19 @@ static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
break;
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
break;
default:
@@ -610,7 +526,7 @@ 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, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+ 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,
@@ -764,8 +680,8 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -777,7 +693,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 48000:
break;
default:
- dev_err(codec->dev, "Unsupported sample rate %d\n",
+ dev_err(component->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
@@ -785,7 +701,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -793,7 +709,7 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
- dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ dev_err(component->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
@@ -804,12 +720,11 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
- dev_err(codec->dev, "Unsupported channels %d\n",
+ 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:
@@ -835,27 +750,27 @@ static int rt286_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
- dev_dbg(codec->dev, "format val = 0x%x\n", val);
+ dev_dbg(component->dev, "format val = 0x%x\n", val);
- snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBP_CFP:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x800);
break;
- case SND_SOC_DAIFMT_CBS_CFS:
- snd_soc_update_bits(codec,
+ case SND_SOC_DAIFMT_CBC_CFC:
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x800, 0x0);
break;
default:
@@ -864,27 +779,27 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
- snd_soc_update_bits(codec,
+ 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_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
- snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_DAC_FORMAT, 0x8000, 0);
+ snd_soc_component_update_bits(component, RT286_ADC_FORMAT, 0x8000, 0);
return 0;
}
@@ -892,58 +807,58 @@ static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+ dev_dbg(component->dev, "%s freq=%d\n", __func__, freq);
if (RT286_SCLK_S_MCLK == clk_id) {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x20);
} else {
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x0100, 0x0100);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL, 0x4, 0x4);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT286_SCLK_S_MCLK == clk_id) {
- dev_err(codec->dev, "Should not use MCLK\n");
+ dev_err(component->dev, "Should not use MCLK\n");
return -EINVAL;
}
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x0);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL2, 0x8, 0x8);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
- dev_err(codec->dev, "Unsupported system clock\n");
+ dev_err(component->dev, "Unsupported system clock\n");
return -EINVAL;
}
@@ -955,53 +870,48 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
- dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ dev_dbg(component->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x1000);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
-static int rt286_set_bias_level(struct snd_soc_codec *codec,
+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 == codec->dapm.bias_level) {
- snd_soc_write(codec,
+ 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_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x200);
}
break;
case SND_SOC_BIAS_ON:
mdelay(10);
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0400);
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT286_DC_GAIN, 0x200, 0x0);
break;
case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec,
+ snd_soc_component_write(component,
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
- snd_soc_update_bits(codec,
- RT286_CBJ_CTRL1, 0x0400, 0x0000);
break;
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -1018,10 +928,10 @@ static irqreturn_t rt286_irq(int irq, void *data)
/* Clear IRQ */
regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x1, 0x1);
- if (hp == true)
+ if (hp)
status |= SND_JACK_HEADPHONE;
- if (mic == true)
+ if (mic)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
@@ -1032,39 +942,31 @@ static irqreturn_t rt286_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt286_probe(struct snd_soc_codec *codec)
+static int rt286_probe(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
- rt286->codec = codec;
- codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+ rt286->component = component;
+ INIT_DELAYED_WORK(&rt286->jack_detect_work, rt286_jack_detect_work);
- if (rt286->i2c->irq) {
- regmap_update_bits(rt286->regmap,
- RT286_IRQ_CTRL, 0x2, 0x2);
-
- 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(1250));
- }
-
+ msecs_to_jiffies(50));
return 0;
}
-static int rt286_remove(struct snd_soc_codec *codec)
+static void rt286_remove(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt286->jack_detect_work);
-
- return 0;
+ rt286->component = NULL;
}
#ifdef CONFIG_PM
-static int rt286_suspend(struct snd_soc_codec *codec)
+static int rt286_suspend(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, true);
regcache_mark_dirty(rt286->regmap);
@@ -1072,12 +974,12 @@ static int rt286_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt286_resume(struct snd_soc_codec *codec)
+static int rt286_resume(struct snd_soc_component *component)
{
- struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt286->regmap, false);
- rt286_index_sync(codec);
+ rt286_index_sync(component);
regcache_sync(rt286->regmap);
return 0;
@@ -1117,7 +1019,7 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
{
.name = "rt286-aif2",
@@ -1137,24 +1039,26 @@ static struct snd_soc_dai_driver rt286_dai[] = {
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
- .probe = rt286_probe,
- .remove = rt286_remove,
- .suspend = rt286_suspend,
- .resume = rt286_resume,
- .set_bias_level = rt286_set_bias_level,
- .idle_bias_off = true,
- .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),
+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 = {
@@ -1163,53 +1067,74 @@ static const struct regmap_config rt286_regmap = {
.max_register = 0x02370100,
.volatile_reg = rt286_volatile_register,
.readable_reg = rt286_readable_register,
- .reg_write = rt286_hw_write,
- .reg_read = rt286_hw_read,
+ .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},
+ {"rt286"},
+ {"rt288"},
{}
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+#ifdef CONFIG_ACPI
static const struct acpi_device_id rt286_acpi_match[] = {
- { "INT343A", 0 },
- {},
+ { "10EC0286" },
+ { "INT343A" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+#endif
-static struct dmi_system_id force_combo_jack_table[] = {
+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 struct dmi_system_id dmi_dell_dino[] = {
+static const struct dmi_system_id dmi_dell[] = {
{
- .ident = "Dell Dino",
+ .ident = "Dell",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_BOARD_NAME, "0144P8")
}
},
{ }
};
-static int rt286_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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;
+ int i, ret, vendor_id;
rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
GFP_KERNEL);
@@ -1224,23 +1149,41 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt286->regmap,
- RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
- if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) {
+ 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", ret);
+ "Device with ID register %#x is not rt286\n",
+ vendor_id);
return -ENODEV;
}
- rt286->index_cache = rt286_index_def;
+ 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 (dmi_check_system(force_combo_jack_table) ||
- dmi_check_system(dmi_dell_dino))
+ 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);
@@ -1263,7 +1206,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
mdelay(10);
if (!rt286->pdata.gpio2_en)
- regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x40);
else
regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
@@ -1279,7 +1222,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
- if (dmi_check_system(dmi_dell_dino)) {
+ 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,
@@ -1295,33 +1238,30 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
- "Failed to reguest IRQ: %d\n", ret);
+ "Failed to request IRQ: %d\n", ret);
return ret;
}
}
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt286,
rt286_dai, ARRAY_SIZE(rt286_dai));
return ret;
}
-static int rt286_i2c_remove(struct i2c_client *i2c)
+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);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
static struct i2c_driver rt286_i2c_driver = {
.driver = {
.name = "rt286",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
.probe = rt286_i2c_probe,
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
index 7130edb152ef..4b7a3bd6043d 100644
--- a/sound/soc/codecs/rt286.h
+++ b/sound/soc/codecs/rt286.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt286.h -- RT286 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * 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 __RT286_H__
@@ -199,7 +196,5 @@ enum {
RT286_AIFS,
};
-int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
-
#endif /* __RT286_H__ */
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
new file mode 100644
index 000000000000..ee3d9291eea6
--- /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 <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt298.h>
+
+#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;
+ }
+
+ 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_CBP_CFP:
+ snd_soc_component_update_bits(component,
+ RT298_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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"},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt298_acpi_match[] = {
+ { "10EC0298" },
+ { "INT343A" },
+ { }
+};
+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 request 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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt298.h b/sound/soc/codecs/rt298.h
new file mode 100644
index 000000000000..f1be9c135401
--- /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 <johnnyhsu@realtek.com>
+ */
+
+#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 000000000000..54d84581ec47
--- /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 <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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 request 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 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 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 = {
+ 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 = pm_ptr(&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 <oder_chiou@realtek.com>");
+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 000000000000..cedb19709c9a
--- /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 <oder_chiou@realtek.com>
+ */
+
+#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 000000000000..ab9d81c32be8
--- /dev/null
+++ b/sound/soc/codecs/rt5514.c
@@ -0,0 +1,1333 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5514.c -- RT5514 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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 (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 = to_platform_device(component->dev);
+
+ rt5514->mclk = devm_clk_get_optional(component->dev, "mclk");
+ if (IS_ERR(rt5514->mclk))
+ return PTR_ERR(rt5514->mclk);
+
+ 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" },
+ { }
+};
+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" },
+ { }
+};
+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 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 = {
+ 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 = pm_ptr(&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 <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
new file mode 100644
index 000000000000..75755599f940
--- /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 <oder_chiou@realtek.com>
+ */
+
+#ifndef __RT5514_H__
+#define __RT5514_H__
+
+#include <linux/clk.h>
+#include <sound/rt5514.h>
+
+#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 000000000000..1ec486707ff9
--- /dev/null
+++ b/sound/soc/codecs/rt5616.c
@@ -0,0 +1,1413 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5616.c -- RT5616 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ rt5616->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 (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_optional(component->dev, "mclk");
+ if (IS_ERR(rt5616->mclk))
+ return PTR_ERR(rt5616->mclk);
+
+ 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" },
+ { }
+};
+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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt5616.h b/sound/soc/codecs/rt5616.h
new file mode 100644
index 000000000000..ad9c5de9052d
--- /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 <johnnyhsu@realtek.com>
+ */
+
+#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
index c61852742ee3..d523477c5102 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5631.c -- RT5631 ALSA Soc Audio driver
*
@@ -6,11 +7,6 @@
* Author: flove <flove@realtek.com>
*
* Based on WM8753.c
- *
- * 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.
- *
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -68,33 +64,33 @@ static const struct reg_default rt5631_reg[] = {
{ RT5631_PSEUDO_SPATL_CTRL, 0x0553 },
};
-/**
+/*
* rt5631_write_index - write index register of 2nd layer
*/
-static void rt5631_write_index(struct snd_soc_codec *codec,
+static void rt5631_write_index(struct snd_soc_component *component,
unsigned int reg, unsigned int value)
{
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- snd_soc_write(codec, RT5631_INDEX_DATA, 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_codec *codec,
+static unsigned int rt5631_read_index(struct snd_soc_component *component,
unsigned int reg)
{
unsigned int value;
- snd_soc_write(codec, RT5631_INDEX_ADD, reg);
- value = snd_soc_read(codec, RT5631_INDEX_DATA);
+ 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_codec *codec)
+static int rt5631_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5631_RESET, 0);
+ return snd_soc_component_write(component, RT5631_RESET, 0);
}
static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
@@ -105,9 +101,9 @@ static bool rt5631_volatile_register(struct device *dev, unsigned int reg)
case RT5631_INDEX_ADD:
case RT5631_INDEX_DATA:
case RT5631_EQ_CTRL:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -164,9 +160,9 @@ static bool rt5631_readable_register(struct device *dev, unsigned int reg)
case RT5631_VENDOR_ID:
case RT5631_VENDOR_ID1:
case RT5631_VENDOR_ID2:
- return 1;
+ return true;
default:
- return 0;
+ return false;
}
}
@@ -174,22 +170,21 @@ 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 unsigned int mic_bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -199,8 +194,8 @@ static int rt5631_dmic_get(struct snd_kcontrol *kcontrol,
static int rt5631_dmic_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -230,10 +225,10 @@ static SOC_ENUM_SINGLE_DECL(rt5631_spk_ratio_enum, RT5631_GEN_PUR_CTRL_REG,
static const struct snd_kcontrol_new rt5631_snd_controls[] = {
/* MIC */
SOC_ENUM("MIC1 Mode Control", rt5631_mic1_mode_enum),
- SOC_SINGLE_TLV("MIC1 Boost", RT5631_MIC_CTRL_2,
+ 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", RT5631_MIC_CTRL_2,
+ 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),
@@ -287,304 +282,308 @@ static const struct snd_kcontrol_new rt5631_snd_controls[] = {
static int check_sysclk1_source(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_GLOBAL_CLK_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_L_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_OUTMIXER_R_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_SPK_MIXER_CTRL);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg;
- reg = snd_soc_read(codec, RT5631_ADC_REC_MIXER);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_TEST_MODE_CTRL, 0x84c0);
- rt5631_write_index(codec, RT5631_SPK_INTL_CTRL, 0x309f);
- rt5631_write_index(codec, RT5631_CP_INTL_REG2, 0x6530);
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_2,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2,
RT5631_EN_CAP_FREE_DEPOP);
} else {
/* power off capless block */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_2, 0);
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_2, 0);
msleep(100);
}
/* recover soft volume and zero crossing setting */
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x307f);
- snd_soc_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_power_stage - step by step depop sequence in power stage.
+ * 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x303e);
+ rt5631_write_index(component, RT5631_SPK_INTL_CTRL, 0x303e);
/* power on headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x303F);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ snd_soc_component_update_bits(component, RT5631_PWR_MANAG_ADD3,
RT5631_PWR_HP_DEPOP_DIS, 0);
/* config depop sequence parameter */
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ snd_soc_component_write(component, RT5631_DEPOP_FUN_CTRL_1,
RT5631_POW_ON_SOFT_GEN);
/* power down headphone and charge pump */
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec, int enable)
+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_update_bits(codec, RT5631_DEPOP_FUN_CTRL_2,
+ 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_read(codec, RT5631_SOFT_VOL_CTRL);
- snd_soc_write(codec, RT5631_SOFT_VOL_CTRL, 0);
- hp_zc = snd_soc_read(codec, RT5631_INT_ST_IRQ_CTRL_2);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc & 0xf7ff);
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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(codec, RT5631_SPK_INTL_CTRL, 0x302f);
- snd_soc_write(codec, RT5631_DEPOP_FUN_CTRL_1,
+ 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_update_bits(codec, RT5631_HP_OUT_VOL,
+ 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_write(codec, RT5631_SOFT_VOL_CTRL, soft_vol);
- snd_soc_write(codec, RT5631_INT_ST_IRQ_CTRL_2, hp_zc);
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, 0);
- onebit_depop_power_stage(codec, 0);
+ onebit_depop_mute_stage(component, 0);
+ onebit_depop_power_stage(component, 0);
} else {
- depop_seq_mute_stage(codec, 0);
- depop_seq_power_stage(codec, 0);
+ 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(codec, 1);
- onebit_depop_mute_stage(codec, 1);
+ onebit_depop_power_stage(component, 1);
+ onebit_depop_mute_stage(component, 1);
} else {
- depop_seq_power_stage(codec, 1);
- depop_seq_mute_stage(codec, 1);
+ depop_seq_power_stage(component, 1);
+ depop_seq_mute_stage(component, 1);
}
break;
@@ -598,20 +597,20 @@ static int hp_event(struct snd_soc_dapm_widget *w,
static int set_dmic_params(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_64FS);
break;
@@ -619,7 +618,7 @@ static int set_dmic_params(struct snd_soc_dapm_widget *w,
case 16000:
case 11025:
case 8000:
- snd_soc_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ snd_soc_component_update_bits(component, RT5631_DIG_MIC_CTRL,
RT5631_DMIC_CLK_CTRL_MASK,
RT5631_DMIC_CLK_CTRL_TO_128FS);
break;
@@ -1284,7 +1283,7 @@ static const struct pll_div codec_slave_pll_div[] = {
{3072000, 12288000, 0x0a90},
};
-static struct coeff_clk_div coeff_div[] = {
+static const struct coeff_clk_div coeff_div[] = {
/* sysclk is 256fs */
{2048000, 8000 * 32, 8000, 0x1000},
{2048000, 8000 * 64, 8000, 0x0000},
@@ -1353,16 +1352,16 @@ static int get_coeff(int mclk, int rate, int timesofbclk)
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_codec *codec = dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
rt5631->bclk_rate = snd_soc_params_to_bclk(params);
if (rt5631->bclk_rate < 0) {
- dev_err(codec->dev, "Fail to get BCLK rate\n");
+ dev_err(component->dev, "Fail to get BCLK rate\n");
return rt5631->bclk_rate;
}
rt5631->rx_rate = params_rate(params);
@@ -1374,7 +1373,7 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
coeff = get_coeff(rt5631->sysclk, rt5631->rx_rate,
timesofbclk);
if (coeff < 0) {
- dev_err(codec->dev, "Fail to get coeff\n");
+ dev_err(component->dev, "Fail to get coeff\n");
return coeff;
}
@@ -1394,9 +1393,9 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5631_SDP_CTRL,
+ snd_soc_component_update_bits(component, RT5631_SDP_CTRL,
RT5631_SDP_I2S_DL_MASK, iface);
- snd_soc_write(codec, RT5631_STEREO_AD_DA_CLK_CTRL,
+ snd_soc_component_write(component, RT5631_STEREO_AD_DA_CLK_CTRL,
coeff_div[coeff].reg_val);
return 0;
@@ -1405,17 +1404,17 @@ static int rt5631_hifi_pcm_params(struct snd_pcm_substream *substream,
static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
unsigned int iface = 0;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5631->master = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
iface |= RT5631_SDP_MODE_SEL_SLAVE;
rt5631->master = 0;
break;
@@ -1449,7 +1448,7 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- snd_soc_write(codec, RT5631_SDP_CTRL, iface);
+ snd_soc_component_write(component, RT5631_SDP_CTRL, iface);
return 0;
}
@@ -1457,10 +1456,10 @@ static int rt5631_hifi_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "enter %s, syclk=%d\n", __func__, freq);
+ dev_dbg(component->dev, "enter %s, syclk=%d\n", __func__, freq);
if ((freq >= (256 * 8000)) && (freq <= (512 * 96000))) {
rt5631->sysclk = freq;
@@ -1473,16 +1472,16 @@ static int rt5631_hifi_codec_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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_codec *codec = codec_dai->codec;
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
int i, ret = -EINVAL;
- dev_dbg(codec->dev, "enter %s\n", __func__);
+ dev_dbg(component->dev, "enter %s\n", __func__);
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
- snd_soc_update_bits(codec, RT5631_GLOBAL_CLK_CTRL,
+ snd_soc_component_update_bits(component, RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK,
RT5631_SYSCLK_SOUR_SEL_MCLK);
@@ -1493,13 +1492,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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(codec->dev,
+ dev_info(component->dev,
"change PLL in master mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_master_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1512,13 +1511,13 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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(codec->dev,
+ dev_info(component->dev,
"change PLL in slave mode\n");
- snd_soc_write(codec, RT5631_PLL_CTRL,
+ snd_soc_component_write(component, RT5631_PLL_CTRL,
codec_slave_pll_div[i].reg_val);
schedule_timeout_uninterruptible(
msecs_to_jiffies(20));
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
RT5631_GLOBAL_CLK_CTRL,
RT5631_SYSCLK_SOUR_SEL_MASK |
RT5631_PLLCLK_SOUR_SEL_MASK,
@@ -1532,26 +1531,26 @@ static int rt5631_codec_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
return ret;
}
-static int rt5631_set_bias_level(struct snd_soc_codec *codec,
+static int rt5631_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD2,
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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);
@@ -1560,62 +1559,61 @@ static int rt5631_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD1, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD2, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD3, 0x0000);
- snd_soc_write(codec, RT5631_PWR_MANAG_ADD4, 0x0000);
+ 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;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int rt5631_probe(struct snd_soc_codec *codec)
+static int rt5631_probe(struct snd_soc_component *component)
{
- struct rt5631_priv *rt5631 = snd_soc_codec_get_drvdata(codec);
+ struct rt5631_priv *rt5631 = snd_soc_component_get_drvdata(component);
unsigned int val;
- val = rt5631_read_index(codec, RT5631_ADDA_MIXER_INTL_REG3);
+ val = rt5631_read_index(component, RT5631_ADDA_MIXER_INTL_REG3);
if (val & 0x0002)
rt5631->codec_version = 1;
else
rt5631->codec_version = 0;
- rt5631_reset(codec);
- snd_soc_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_update_bits(codec, RT5631_PWR_MANAG_ADD3,
+ 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_write(codec, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
+ snd_soc_component_write(component, RT5631_INT_ST_IRQ_CTRL_2, 0x0f18);
/* power off ClassD auto Recovery */
if (rt5631->codec_version)
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0x2000);
else
- snd_soc_update_bits(codec, RT5631_INT_ST_IRQ_CTRL_2,
+ snd_soc_component_update_bits(component, RT5631_INT_ST_IRQ_CTRL_2,
0x2000, 0);
/* DMIC */
if (rt5631->dmic_used_flag) {
- snd_soc_update_bits(codec, RT5631_GPIO_CTRL,
+ 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_update_bits(codec, RT5631_DIG_MIC_CTRL,
+ 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);
}
- codec->dapm.bias_level = SND_SOC_BIAS_STANDBY;
+ snd_soc_component_init_bias_level(component, SND_SOC_BIAS_STANDBY);
return 0;
}
@@ -1655,27 +1653,30 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
- .probe = rt5631_probe,
- .set_bias_level = rt5631_set_bias_level,
- .suspend_bias_off = true,
- .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),
+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 },
+ { "rt5631" },
+ { "alc5631" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
#ifdef CONFIG_OF
-static struct of_device_id rt5631_i2c_dt_ids[] = {
+static const struct of_device_id rt5631_i2c_dt_ids[] = {
{ .compatible = "realtek,rt5631"},
{ .compatible = "realtek,alc5631"},
{ }
@@ -1692,11 +1693,12 @@ static const struct regmap_config rt5631_regmap_config = {
.max_register = RT5631_VENDOR_ID2,
.reg_defaults = rt5631_reg,
.num_reg_defaults = ARRAY_SIZE(rt5631_reg),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
};
-static int rt5631_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5631_i2c_probe(struct i2c_client *i2c)
{
struct rt5631_priv *rt5631;
int ret;
@@ -1712,24 +1714,21 @@ static int rt5631_i2c_probe(struct i2c_client *i2c,
if (IS_ERR(rt5631->regmap))
return PTR_ERR(rt5631->regmap);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5631,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5631,
rt5631_dai, ARRAY_SIZE(rt5631_dai));
return ret;
}
-static int rt5631_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+static void rt5631_i2c_remove(struct i2c_client *client)
+{}
static struct i2c_driver rt5631_i2c_driver = {
.driver = {
.name = "rt5631",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
- .probe = rt5631_i2c_probe,
+ .probe = rt5631_i2c_probe,
.remove = rt5631_i2c_remove,
.id_table = rt5631_i2c_id,
};
diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h
index 13401581b0df..8a6b99a48c7c 100644
--- a/sound/soc/codecs/rt5631.h
+++ b/sound/soc/codecs/rt5631.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __RTCODEC5631_H__
#define __RTCODEC5631_H__
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 178e55d4d481..f50e771db24b 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5640.c -- RT5640/RT5639 ALSA SoC audio codec driver
*
* Copyright 2011 Realtek Semiconductor Corp.
* Author: Johnny Hsu <johnnyhsu@realtek.com>
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
- *
- * 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.
*/
#include <linux/module.h>
@@ -15,15 +12,15 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
#include <sound/core.h>
+#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -51,15 +48,13 @@ static const struct regmap_range_cfg rt5640_ranges[] = {
.window_len = 0x1, },
};
-static struct reg_default init_list[] = {
+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 + 0x20, 0x6110},
{RT5640_PR_BASE + 0x21, 0xe0e0},
{RT5640_PR_BASE + 0x23, 0x1804},
};
-#define RT5640_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5640_reg[] = {
{ 0x00, 0x000e },
@@ -167,9 +162,9 @@ static const struct reg_default rt5640_reg[] = {
{ 0xff, 0x6231 },
};
-static int rt5640_reset(struct snd_soc_codec *codec)
+static int rt5640_reset(struct snd_soc_component *component)
{
- return snd_soc_write(codec, RT5640_RESET, 0);
+ return snd_soc_component_write(component, RT5640_RESET, 0);
}
static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
@@ -198,6 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
+ case RT5640_GCTL2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -329,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
case RT5640_HP_CALIB2:
case RT5640_SV_ZCD1:
case RT5640_SV_ZCD2:
- case RT5640_DUMMY1:
- case RT5640_DUMMY2:
+ case RT5640_GCTL1:
+ case RT5640_GCTL2:
case RT5640_DUMMY3:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
@@ -342,26 +338,25 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
}
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_MINMAX(dac_vol_tlv, -6562, 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_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 unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5640_data_select[] = {
- "Normal", "left copy to right", "right copy to left", "Swap"};
+ "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);
@@ -404,14 +399,20 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
/* 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 Control */
+ /* 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,
@@ -422,6 +423,8 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
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_GCTL1,
+ 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),
@@ -442,9 +445,6 @@ 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),
-
- SOC_DOUBLE_TLV("Mono DAC Playback Volume", RT5640_DAC2_DIG_VOL,
- RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 175, 0, dac_vol_tlv),
};
/**
@@ -458,32 +458,31 @@ static const struct snd_kcontrol_new rt5640_specific_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5640->sysclk);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5640_DMIC, RT5640_DMIC_CLK_MASK,
idx << RT5640_DMIC_CLK_SFT);
return idx;
}
-static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- val = snd_soc_read(codec, RT5640_GLB_CLK);
- val &= RT5640_SCLK_SRC_MASK;
- if (val == RT5640_SCLK_SRC_PLL1)
- return 1;
- else
+ if (!rt5640->asrc_en)
return 0;
+
+ return 1;
}
/* Digital Mixer */
@@ -599,6 +598,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
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,
@@ -612,6 +613,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
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,
@@ -910,9 +913,9 @@ static SOC_ENUM_SINGLE_DECL(rt5640_sdi_sel_enum, RT5640_I2S2_SDP,
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_codec *codec)
+static void hp_amp_power_on(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
/* depop parameters */
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
@@ -936,9 +939,9 @@ static void hp_amp_power_on(struct snd_soc_codec *codec)
RT5640_PWR_FV1 | RT5640_PWR_FV2);
}
-static void rt5640_pmu_depop(struct snd_soc_codec *codec)
+static void rt5640_pmu_depop(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -964,18 +967,47 @@ static void rt5640_pmu_depop(struct snd_soc_codec *codec)
static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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:
- rt5640_pmu_depop(codec);
- rt5640->hp_mute = 0;
+ 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:
- rt5640->hp_mute = 1;
- usleep_range(70000, 75000);
+ 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:
@@ -988,11 +1020,11 @@ static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power_on(codec);
+ hp_amp_power_on(component);
break;
default:
return 0;
@@ -1004,13 +1036,13 @@ static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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)
- usleep_range(80000, 85000);
+ msleep(80);
break;
@@ -1022,8 +1054,19 @@ static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
}
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
- SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
- RT5640_PWR_PLL_BIT, 0, NULL, 0),
+ /* 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,
@@ -1037,6 +1080,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
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),
@@ -1053,6 +1098,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
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),
@@ -1152,11 +1199,18 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
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, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L1_BIT, 0),
- SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R1_BIT, 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 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)),
@@ -1180,13 +1234,16 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
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", RT5640_PWR_ANLG1, RT5640_PWR_LM_BIT, 0,
+ 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,
@@ -1230,9 +1287,9 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
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, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+ SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0,
0),
- SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+ 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,
@@ -1260,11 +1317,6 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
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_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1,
- RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
-
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,
@@ -1277,8 +1329,15 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
};
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"},
@@ -1289,18 +1348,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"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"},
@@ -1344,22 +1407,18 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"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 Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"Stereo ADC MIXR", "ADC1 Switch", "Stereo ADC R1 Mux"},
{"Stereo ADC MIXR", "ADC2 Switch", "Stereo ADC R2 Mux"},
{"Stereo ADC MIXR", NULL, "Stereo Filter"},
- {"Stereo 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, "Mono Left Filter"},
- {"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, "Mono Right Filter"},
- {"Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll},
{"IF2 ADC L", NULL, "Mono ADC MIXL"},
{"IF2 ADC R", NULL, "Mono ADC MIXR"},
@@ -1414,8 +1473,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"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"},
@@ -1427,9 +1488,9 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"DIG MIXR", "DAC R1 Switch", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R1", NULL, "DAC R1 Power"},
{"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
{"SPK MIXL", "INL Switch", "INL VOL"},
@@ -1501,8 +1562,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"HP R Playback", "Switch", "HP Amp"},
{"HPOL", NULL, "HP L Playback"},
{"HPOR", NULL, "HP R Playback"},
- {"LOUTL", NULL, "LOUT MIX"},
- {"LOUTR", NULL, "LOUT MIX"},
+
+ {"LOUT amp", NULL, "LOUT MIX"},
+ {"LOUTL", NULL, "LOUT amp"},
+ {"LOUTR", NULL, "LOUT amp"},
};
static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
@@ -1514,8 +1577,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"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"},
@@ -1532,9 +1596,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "DAC L2 Mux"},
{"DAC L2", NULL, "Mono DAC MIXL"},
- {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC L2", NULL, "DAC L2 Power"},
{"DAC R2", NULL, "Mono DAC MIXR"},
- {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+ {"DAC R2", NULL, "DAC R2 Power"},
{"SPK MIXL", "DAC L2 Switch", "DAC L2"},
{"SPK MIXR", "DAC R2 Switch", "DAC R2"},
@@ -1575,18 +1639,18 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
{"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
{"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
- {"IF2 DAC L", NULL, "DAC L2 Filter"},
- {"IF2 DAC R", NULL, "DAC R2 Filter"},
+ {"IF2 DAC L", NULL, "DAC L2 Power"},
+ {"IF2 DAC R", NULL, "DAC R2 Power"},
};
-static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
+static int get_sdp_info(struct snd_soc_component *component, int dai_id)
{
int ret = 0, val;
- if (codec == NULL)
+ if (component == NULL)
return -EINVAL;
- val = snd_soc_read(codec, RT5640_I2S1_SDP);
+ 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:
@@ -1597,6 +1661,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_113:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_312:
case RT5640_IF_213:
ret |= RT5640_U_IF2;
@@ -1612,6 +1677,7 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
break;
case RT5640_IF_223:
ret |= RT5640_U_IF1;
+ fallthrough;
case RT5640_IF_123:
case RT5640_IF_321:
ret |= RT5640_U_IF2;
@@ -1630,21 +1696,21 @@ static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
static int rt5640_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return frame_size;
}
if (frame_size > 32)
@@ -1674,26 +1740,26 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ 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_update_bits(codec, RT5640_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S1_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ 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_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5640_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5640_ADDA_CLK1, mask_clk, val_clk);
}
return 0;
@@ -1701,16 +1767,16 @@ static int rt5640_hw_params(struct snd_pcm_substream *substream,
static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5640->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5640_I2S_MS_S;
rt5640->master[dai->id] = 0;
break;
@@ -1744,18 +1810,18 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- dai_sel = get_sdp_info(codec, dai->id);
+ dai_sel = get_sdp_info(component, dai->id);
if (dai_sel < 0) {
- dev_err(codec->dev, "Failed to get sdp info: %d\n", dai_sel);
+ dev_err(component->dev, "Failed to get sdp info: %d\n", dai_sel);
return -EINVAL;
}
if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_I2S1_SDP,
+ 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_update_bits(codec, RT5640_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5640_I2S2_SDP,
RT5640_I2S_MS_MASK | RT5640_I2S_BP_MASK |
RT5640_I2S_DF_MASK, reg_val);
}
@@ -1766,25 +1832,34 @@ static int rt5640_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
-
- if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
- return 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(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ 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;
@@ -1796,67 +1871,58 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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, dai_sel;
+ 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(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5640->pll_in = 0;
rt5640->pll_out = 0;
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
+ 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_update_bits(codec, RT5640_GLB_CLK,
+ 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:
- dai_sel = get_sdp_info(codec, dai->id);
- if (dai_sel < 0) {
- dev_err(codec->dev,
- "Failed to get sdp info: %d\n", dai_sel);
- return -EINVAL;
- }
- if (dai_sel & RT5640_U_IF1) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
- }
- if (dai_sel & RT5640_U_IF2) {
- snd_soc_update_bits(codec, RT5640_GLB_CLK,
- RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
- }
+ snd_soc_component_update_bits(component, RT5640_GLB_CLK,
+ RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5640_PLL_CTRL1,
- pll_code.n_code << RT5640_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, 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);
+ 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;
@@ -1865,52 +1931,79 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5640_set_bias_level(struct snd_soc_codec *codec,
+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 (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 == codec->dapm.bias_level) {
- snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
+ 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_update_bits(codec, RT5640_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
- snd_soc_update_bits(codec, RT5640_DUMMY1,
- 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_MICBIAS,
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
+ 0x1, 0x1);
+ snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
}
break;
case SND_SOC_BIAS_OFF:
- snd_soc_write(codec, RT5640_DEPOP_M1, 0x0004);
- snd_soc_write(codec, RT5640_DEPOP_M2, 0x1100);
- snd_soc_update_bits(codec, RT5640_DUMMY1, 0x1, 0);
- snd_soc_write(codec, RT5640_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5640_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5640_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5640_PWR_ANLG2, 0x0000);
+ 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_GCTL1, 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;
}
- codec->dapm.bias_level = level;
return 0;
}
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+int rt5640_dmic_enable(struct snd_soc_component *component,
bool dmic1_data_pin, bool dmic2_data_pin)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -1933,86 +2026,837 @@ int rt5640_dmic_enable(struct snd_soc_codec *codec,
}
EXPORT_SYMBOL_GPL(rt5640_dmic_enable);
-static int rt5640_probe(struct snd_soc_codec *codec)
+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_codec_get_drvdata(codec);
+ 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;
+ }
- rt5640->codec = codec;
+ 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);
+ }
- rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ 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_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
- snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
- snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
+ snd_soc_component_update_bits(component, RT5640_ASRC_2,
+ asrc2_mask, asrc2_value);
- switch (snd_soc_read(codec, RT5640_RESET) & RT5640_ID_MASK) {
+ 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_GCTL2);
+ 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_GCTL2,
+ (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_disable_irq_and_cancel_work(void *data)
+{
+ struct rt5640_priv *rt5640 = data;
+
+ if (rt5640->jd_gpio_irq_requested) {
+ free_irq(rt5640->jd_gpio_irq, rt5640);
+ rt5640->jd_gpio_irq_requested = false;
+ }
+
+ if (rt5640->irq_requested) {
+ free_irq(rt5640->irq, rt5640);
+ rt5640->irq_requested = false;
+ }
+
+ 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;
+
+ rt5640_disable_irq_and_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 = 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_GCTL1, 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_GCTL2,
+ 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_GCTL2,
+ 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_GCTL2, 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_GCTL1, 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_optional(component->dev, "mclk");
+ if (IS_ERR(rt5640->mclk))
+ return PTR_ERR(rt5640->mclk);
+
+ rt5640->component = component;
+
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
+
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 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_codec_controls(codec,
+ snd_soc_add_component_controls(component,
rt5640_specific_snd_controls,
ARRAY_SIZE(rt5640_specific_snd_controls));
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5640_specific_dapm_widgets,
ARRAY_SIZE(rt5640_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ 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(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5639_specific_dapm_widgets,
ARRAY_SIZE(rt5639_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5639_specific_dapm_routes,
ARRAY_SIZE(rt5639_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5639 RT5640 or RT5642 only\n");
return -ENODEV;
}
- if (rt5640->pdata.dmic_en)
- rt5640_dmic_enable(codec, rt5640->pdata.dmic1_data_pin,
- rt5640->pdata.dmic2_data_pin);
+ /*
+ * 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_GCTL1,
+ 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 int rt5640_remove(struct snd_soc_codec *codec)
+static void rt5640_remove(struct snd_soc_component *component)
{
- rt5640_reset(codec);
-
- return 0;
+ rt5640_reset(component);
}
#ifdef CONFIG_PM
-static int rt5640_suspend(struct snd_soc_codec *codec)
+static int rt5640_suspend(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
+
+ if (rt5640->jack) {
+ /* disable jack interrupts during system suspend */
+ disable_irq(rt5640->irq);
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ cancel_delayed_work_sync(&rt5640->bp_work);
+ }
- rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
- rt5640_reset(codec);
+ 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 (gpio_is_valid(rt5640->pdata.ldo1_en))
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 0);
+ if (rt5640->ldo1_en)
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 0);
return 0;
}
-static int rt5640_resume(struct snd_soc_codec *codec)
+static int rt5640_resume(struct snd_soc_component *component)
{
- struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+ struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- gpio_set_value_cansleep(rt5640->pdata.ldo1_en, 1);
+ 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_GCTL2, 0x1100, 0x1100);
+ } else {
+ if (rt5640->jd_inverted) {
+ if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
+ snd_soc_component_update_bits(
+ component, RT5640_GCTL2,
+ 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_GCTL2,
+ 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
@@ -2072,32 +2916,35 @@ static struct snd_soc_dai_driver rt5640_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
- .probe = rt5640_probe,
- .remove = rt5640_remove,
- .suspend = rt5640_suspend,
- .resume = rt5640_resume,
- .set_bias_level = rt5640_set_bias_level,
- .idle_bias_off = true,
- .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),
+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_rw = true,
+ .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_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5640_reg,
.num_reg_defaults = ARRAY_SIZE(rt5640_reg),
.ranges = rt5640_ranges,
@@ -2105,9 +2952,9 @@ static const struct regmap_config rt5640_regmap = {
};
static const struct i2c_device_id rt5640_i2c_id[] = {
- { "rt5640", 0 },
- { "rt5639", 0 },
- { "rt5642", 0 },
+ { "rt5640" },
+ { "rt5639" },
+ { "rt5642" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
@@ -2116,47 +2963,25 @@ MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
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 struct acpi_device_id rt5640_acpi_match[] = {
- { "INT33CA", 0 },
- { "10EC5640", 0 },
- { "10EC5642", 0 },
- { },
+static const struct acpi_device_id rt5640_acpi_match[] = {
+ { "10EC3276" },
+ { "10EC5640" },
+ { "10EC5642" },
+ { "INT33CA" },
+ { "INTCCFFD" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
#endif
-static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
-{
- rt5640->pdata.in1_diff = of_property_read_bool(np,
- "realtek,in1-differential");
- rt5640->pdata.in2_diff = of_property_read_bool(np,
- "realtek,in2-differential");
-
- rt5640->pdata.ldo1_en = of_get_named_gpio(np,
- "realtek,ldo1-en-gpios", 0);
- /*
- * LDO1_EN is optional (it may be statically tied on the board).
- * -ENOENT means that the property doesn't exist, i.e. there is no
- * GPIO, so is not an error. Any other error code means the property
- * exists, but could not be parsed.
- */
- if (!gpio_is_valid(rt5640->pdata.ldo1_en) &&
- (rt5640->pdata.ldo1_en != -ENOENT))
- return rt5640->pdata.ldo1_en;
-
- return 0;
-}
-
-static int rt5640_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5640_i2c_probe(struct i2c_client *i2c)
{
- struct rt5640_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5640_priv *rt5640;
int ret;
unsigned int val;
@@ -2168,22 +2993,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
- if (pdata) {
- rt5640->pdata = *pdata;
- /*
- * Translate zero'd out (default) pdata value to an invalid
- * GPIO ID. This makes the pdata and DT paths consistent in
- * terms of the value left in this field when no GPIO is
- * specified, but means we can't actually use GPIO 0.
- */
- if (!rt5640->pdata.ldo1_en)
- rt5640->pdata.ldo1_en = -EINVAL;
- } else if (i2c->dev.of_node) {
- ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
- if (ret)
- return ret;
- } else
- rt5640->pdata.ldo1_en = -EINVAL;
+ 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)) {
@@ -2193,22 +3012,15 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
return ret;
}
- if (gpio_is_valid(rt5640->pdata.ldo1_en)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5640->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH,
- "RT5640 LDO1_EN");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
- rt5640->pdata.ldo1_en, ret);
- return ret;
- }
- msleep(400);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ if (val != RT5640_DEVICE_ID) {
+ usleep_range(60000, 100000);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
}
- 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);
+ "Device with ID register %#x is not rt5640/39\n", val);
return -ENODEV;
}
@@ -2219,36 +3031,31 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5640->pdata.in1_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
- RT5640_IN_DF1, RT5640_IN_DF1);
+ regmap_update_bits(rt5640->regmap, RT5640_GCTL1,
+ RT5640_MCLK_DET, RT5640_MCLK_DET);
- if (rt5640->pdata.in2_diff)
- regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
- RT5640_IN_DF2, RT5640_IN_DF2);
+ 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);
- rt5640->hp_mute = 1;
+ /* Make sure work is stopped on probe-error / remove */
+ ret = devm_add_action_or_reset(&i2c->dev, rt5640_disable_irq_and_cancel_work, rt5640);
+ if (ret)
+ return ret;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai));
}
-static int rt5640_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5640_i2c_driver = {
.driver = {
.name = "rt5640",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5640_acpi_match),
.of_match_table = of_match_ptr(rt5640_of_match),
},
.probe = rt5640_i2c_probe,
- .remove = rt5640_i2c_remove,
.id_table = rt5640_i2c_id,
};
module_i2c_driver(rt5640_i2c_driver);
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 3deb8babeabb..8a12cee76bdc 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -1,18 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5640.h -- RT5640 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * 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 _RT5640_H
#define _RT5640_H
-#include <sound/rt5640.h>
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/workqueue.h>
+#include <dt-bindings/sound/rt5640.h>
/* Info */
#define RT5640_RESET 0x00
@@ -139,12 +139,13 @@
#define RT5640_SV_ZCD1 0xd9
#define RT5640_SV_ZCD2 0xda
/* Dummy Register */
-#define RT5640_DUMMY1 0xfa
-#define RT5640_DUMMY2 0xfb
+#define RT5640_GCTL1 0xfa
+#define RT5640_GCTL2 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
@@ -442,39 +443,39 @@
#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_L2R (0x1 << 14)
-#define RT5640_IF1_DAC_SEL_R2L (0x2 << 14)
-#define RT5640_IF1_DAC_SEL_SWAP (0x3 << 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_L2R (0x1 << 12)
-#define RT5640_IF1_ADC_SEL_R2L (0x2 << 12)
-#define RT5640_IF1_ADC_SEL_SWAP (0x3 << 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_L2R (0x1 << 10)
-#define RT5640_IF2_DAC_SEL_R2L (0x2 << 10)
-#define RT5640_IF2_DAC_SEL_SWAP (0x3 << 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_L2R (0x1 << 8)
-#define RT5640_IF2_ADC_SEL_R2L (0x2 << 8)
-#define RT5640_IF2_ADC_SEL_SWAP (0x3 << 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_L2R (0x1 << 6)
-#define RT5640_IF3_DAC_SEL_R2L (0x2 << 6)
-#define RT5640_IF3_DAC_SEL_SWAP (0x3 << 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_L2R (0x1 << 4)
-#define RT5640_IF3_ADC_SEL_R2L (0x2 << 4)
-#define RT5640_IF3_ADC_SEL_SWAP (0x3 << 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)
@@ -983,6 +984,7 @@
#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)
@@ -1033,6 +1035,10 @@
#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
@@ -1601,10 +1607,17 @@
#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_CLR (0x1 << 3)
-#define RT5640_MB1_OC_CLR_SFT 3
-#define RT5640_MB2_OC_CLR (0x1 << 2)
-#define RT5640_MB2_OC_CLR_SFT 2
+#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)
@@ -1964,8 +1977,39 @@
#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 2 (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
@@ -2079,11 +2123,24 @@ enum {
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_codec *codec;
- struct rt5640_platform_data pdata;
+ 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];
@@ -2095,9 +2152,42 @@ struct rt5640_priv {
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;
};
-int rt5640_dmic_enable(struct snd_soc_codec *codec,
+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
index c9a4c5be083b..29a403526cd9 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5645.c -- RT5645 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -17,7 +14,10 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -30,6 +30,20 @@
#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
@@ -38,6 +52,10 @@
#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",
@@ -51,17 +69,20 @@ static const struct regmap_range_cfg rt5645_ranges[] = {
},
};
-static const struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x3d, 0x3600},
- {RT5645_PR_BASE + 0x1c, 0xfd20},
+ {RT5645_PR_BASE + 0x1c, 0xfd70},
{RT5645_PR_BASE + 0x20, 0x611f},
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
+ {RT5645_ASRC_4, 0x0120},
};
-#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
-static const struct reg_default rt5650_init_list[] = {
+static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
+ {RT5645_PWR_ANLG1, 0x02},
+ {RT5645_IL_CMD3, 0x6728},
+ {RT5645_PR_BASE + 0x3a, 0x0000},
};
static const struct reg_default rt5645_reg[] = {
@@ -152,7 +173,164 @@ static const struct reg_default rt5645_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 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 },
@@ -220,9 +398,83 @@ static const struct reg_default rt5645_reg[] = {
{ 0xff, 0x6308 },
};
-static int rt5645_reset(struct snd_soc_codec *codec)
+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;
+
+ /* Only 1 speaker connected */
+ bool mono_speaker;
+
+ /* 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 gpio_desc *gpiod_cbj_sleeve;
+ 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_write(codec, RT5645_RESET, 0);
+ return snd_soc_component_write(component, RT5645_RESET, 0);
}
static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
@@ -238,6 +490,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case RT5645_RESET:
+ case RT5645_PRIV_INDEX:
case RT5645_PRIV_DATA:
case RT5645_IN1_CTRL1:
case RT5645_IN1_CTRL2:
@@ -346,6 +599,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
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:
@@ -356,6 +610,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
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:
@@ -414,58 +669,149 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
}
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(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, -17625, 375, 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 unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 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);
-static const char * const rt5645_tdm_data_swap_select[] = {
- "L/R", "R/L", "L/L", "R/R"
-};
+ return 0;
+}
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot0_1_enum,
- RT5645_TDM_CTRL_1, 6, rt5645_tdm_data_swap_select);
+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;
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot2_3_enum,
- RT5645_TDM_CTRL_1, 4, rt5645_tdm_data_swap_select);
+ 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);
+ }
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot4_5_enum,
- RT5645_TDM_CTRL_1, 2, rt5645_tdm_data_swap_select);
+ 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);
-static SOC_ENUM_SINGLE_DECL(rt5645_tdm_adc_slot6_7_enum,
- RT5645_TDM_CTRL_1, 0, rt5645_tdm_data_swap_select);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
-static const char * const rt5645_tdm_adc_data_select[] = {
- "1/2/R", "2/1/R", "R/1/2", "R/2/1"
+ 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_tdm_adc_sel_enum,
- RT5645_TDM_CTRL_1, 8,
- rt5645_tdm_adc_data_select);
+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_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
- RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
+ 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("HP Channel Switch", RT5645_HP_VOL,
+ SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
- SOC_DOUBLE_TLV("HP Playback Volume", RT5645_HP_VOL,
+ SOC_DOUBLE_TLV("Headphone Playback Volume", RT5645_HP_VOL,
RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
/* OUTPUT Control */
@@ -480,13 +826,13 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
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, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ 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, RT5645_R_VOL_SFT, 175, 0, dac_vol_tlv),
+ 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, 8, 0, bst_tlv),
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
RT5645_BST_SFT2, 8, 0, bst_tlv),
@@ -498,34 +844,27 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
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, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ 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, RT5645_R_VOL_SFT, 127, 0, adc_vol_tlv),
+ RT5645_L_VOL_SFT + 1, RT5645_R_VOL_SFT + 1, 63, 0, adc_vol_tlv),
/* ADC Boost Volume Control */
- SOC_DOUBLE_TLV("STO1 ADC Boost Gain", RT5645_ADC_BST_VOL1,
+ 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("STO2 ADC Boost Gain", RT5645_ADC_BST_VOL1,
- RT5645_STO2_ADC_L_BST_SFT, RT5645_STO2_ADC_R_BST_SFT, 3, 0,
+ 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"),
- /* TDM */
- SOC_ENUM("TDM Adc Slot0 1 Data", rt5645_tdm_adc_slot0_1_enum),
- SOC_ENUM("TDM Adc Slot2 3 Data", rt5645_tdm_adc_slot2_3_enum),
- SOC_ENUM("TDM Adc Slot4 5 Data", rt5645_tdm_adc_slot4_5_enum),
- SOC_ENUM("TDM Adc Slot6 7 Data", rt5645_tdm_adc_slot6_7_enum),
- SOC_ENUM("TDM IF1 ADC DATA Sel", rt5645_tdm_adc_sel_enum),
- SOC_SINGLE("TDM IF1_DAC1_L Sel", RT5645_TDM_CTRL_3, 12, 7, 0),
- SOC_SINGLE("TDM IF1_DAC1_R Sel", RT5645_TDM_CTRL_3, 8, 7, 0),
- SOC_SINGLE("TDM IF1_DAC2_L Sel", RT5645_TDM_CTRL_3, 4, 7, 0),
- SOC_SINGLE("TDM IF1_DAC2_R Sel", RT5645_TDM_CTRL_3, 0, 7, 0),
+ /* Digital Soft Volume Control */
+ SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
};
/**
@@ -539,16 +878,17 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5645->sysclk);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5645_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5645_DMIC_CTRL1,
RT5645_DMIC_CLK_MASK, idx << RT5645_DMIC_CLK_SFT);
return idx;
}
@@ -556,10 +896,10 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int val;
- val = snd_soc_read(codec, RT5645_GLB_CLK);
+ val = snd_soc_component_read(component, RT5645_GLB_CLK);
val &= RT5645_SCLK_SRC_MASK;
if (val == RT5645_SCLK_SRC_PLL1)
return 1;
@@ -570,7 +910,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -602,7 +942,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -615,9 +955,25 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
}
+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
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -629,7 +985,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
* 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_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src)
{
unsigned int asrc2_mask = 0;
@@ -685,11 +1041,11 @@ int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_2,
+ snd_soc_component_update_bits(component, RT5645_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5645_ASRC_3,
+ snd_soc_component_update_bits(component, RT5645_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
@@ -728,14 +1084,14 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r_mix[] = {
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("DAC1 Switch", RT5645_AD_DA_MIXER,
+ 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("DAC1 Switch", RT5645_AD_DA_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 Switch", RT5645_AD_DA_MIXER,
RT5645_M_DAC1_R_SFT, 1, 1),
};
@@ -964,30 +1320,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_dac_r2_mux =
SOC_DAPM_ENUM("DAC2 R source", rt5645_dac2r_enum);
-
-/* INL/R source */
-static const char * const rt5645_inl_src[] = {
- "IN2P", "MonoP"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inl_enum, RT5645_INL1_INR1_VOL,
- RT5645_INL_SEL_SFT, rt5645_inl_src);
-
-static const struct snd_kcontrol_new rt5645_inl_mux =
- SOC_DAPM_ENUM("INL source", rt5645_inl_enum);
-
-static const char * const rt5645_inr_src[] = {
- "IN2N", "MonoN"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_inr_enum, RT5645_INL1_INR1_VOL,
- RT5645_INR_SEL_SFT, rt5645_inr_src);
-
-static const struct snd_kcontrol_new rt5645_inr_mux =
- SOC_DAPM_ENUM("INR source", rt5645_inr_enum);
-
/* Stereo1 ADC source */
/* MX-27 [12] */
static const char * const rt5645_stereo_adc1_src[] = {
@@ -1092,7 +1424,8 @@ static const struct snd_kcontrol_new rt5645_mono_adc_r2_mux =
/* MX-77 [9:8] */
static const char * const rt5645_if1_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "VAD_ADC"
+ "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(
@@ -1102,6 +1435,140 @@ static SOC_ENUM_SINGLE_DECL(
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"
@@ -1152,18 +1619,6 @@ static SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5645_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5645_if2_adc_in_enum);
-/* MX-2F [1:0] */
-static const char * const rt5645_if3_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "VAD_ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5645_if3_adc_in_enum, RT5645_DIG_INF1_DATA,
- RT5645_IF3_ADC_IN_SFT, rt5645_if3_adc_in_src);
-
-static const struct snd_kcontrol_new rt5645_if3_adc_in_mux =
- SOC_DAPM_ENUM("IF3 ADC IN source", rt5645_if3_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5645_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1219,57 +1674,104 @@ 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_codec *codec, int on)
+static void hp_amp_power(struct snd_soc_component *component, int on)
{
static int hp_amp_power_count;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+ int i, val;
if (on) {
if (hp_amp_power_count <= 0) {
- /* depop parameters */
- snd_soc_update_bits(codec, RT5645_DEPOP_M2,
- RT5645_DEPOP_MASK, RT5645_DEPOP_MAN);
- snd_soc_write(codec, RT5645_DEPOP_M1, 0x000d);
- regmap_write(rt5645->regmap, RT5645_PR_BASE +
- RT5645_HP_DCC_INT1, 0x9f01);
- mdelay(150);
- /* headphone amp power on */
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_FV1 | RT5645_PWR_FV2 , 0);
- snd_soc_update_bits(codec, RT5645_PWR_VOL,
- RT5645_PWR_HV_L | RT5645_PWR_HV_R,
- RT5645_PWR_HV_L | RT5645_PWR_HV_R);
- snd_soc_update_bits(codec, 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_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_FV1 | RT5645_PWR_FV2,
- RT5645_PWR_FV1 | RT5645_PWR_FV2);
-
- snd_soc_update_bits(codec, 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);
+ 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) {
- snd_soc_update_bits(codec, 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_write(codec, RT5645_DEPOP_M1, 0x0000);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
- RT5645_PWR_HP_L | RT5645_PWR_HP_R |
- RT5645_PWR_HA, 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);
+ }
}
}
}
@@ -1277,64 +1779,60 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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(codec, 1);
+ hp_amp_power(component, 1);
/* headphone unmute sequence */
- if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
- } else {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ 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);
}
- regmap_write(rt5645->regmap,
- RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_SMT_TRIG_MASK, RT5645_SMT_TRIG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTN_MASK, RT5645_RSTN_EN);
- snd_soc_update_bits(codec, 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_update_bits(codec, 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_RT5650) {
- snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
- } else {
- snd_soc_update_bits(codec, RT5645_DEPOP_M3,
+ 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);
}
- regmap_write(rt5645->regmap,
- RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_HP_SG_MASK, RT5645_HP_SG_EN);
- snd_soc_update_bits(codec, RT5645_DEPOP_M1,
- RT5645_RSTP_MASK, RT5645_RSTP_EN);
- snd_soc_update_bits(codec, 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(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1347,19 +1845,25 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5645_PWR_DIG1,
+ 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_update_bits(codec, RT5645_PWR_DIG1,
+ 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;
@@ -1374,24 +1878,24 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- hp_amp_power(codec, 1);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ hp_amp_power(component, 1);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, RT5645_PWR_LM);
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE, 0);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(codec, RT5645_LOUT1,
+ snd_soc_component_update_bits(component, RT5645_LOUT1,
RT5645_L_MUTE | RT5645_R_MUTE,
RT5645_L_MUTE | RT5645_R_MUTE);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_LM, 0);
- hp_amp_power(codec, 0);
+ hp_amp_power(component, 0);
break;
default:
@@ -1404,16 +1908,16 @@ static int rt5645_lout_event(struct snd_soc_dapm_widget *w,
static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5645_PWR_ANLG2,
+ 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_update_bits(codec, RT5645_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG2,
RT5645_PWR_BST2_P, 0);
break;
@@ -1424,6 +1928,56 @@ static int rt5645_bst2_event(struct snd_soc_dapm_widget *w,
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),
@@ -1461,10 +2015,12 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
/* Input Side */
/* micbias */
- SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
- RT5645_PWR_MB1_BIT, 0),
- SND_SOC_DAPM_MICBIAS("micbias2", RT5645_PWR_ANLG2,
- RT5645_PWR_MB2_BIT, 0),
+ 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"),
@@ -1538,8 +2094,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2,
RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY_S("adc stereo2 filter", 1, RT5645_PWR_DIG2,
- RT5645_PWR_ADC_S2F_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),
@@ -1570,20 +2124,16 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
/* IF1 2 Mux */
- SND_SOC_DAPM_MUX("IF1 ADC Mux", SND_SOC_NOPM,
- 0, 0, &rt5645_if1_adc_in_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 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", 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),
@@ -1716,6 +2266,25 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
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),
@@ -1725,11 +2294,28 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
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 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 },
@@ -1848,42 +2434,32 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "IF_ADC2", NULL, "Mono ADC MIXR" },
{ "VAD_ADC", NULL, "VAD ADC Mux" },
- { "IF1 ADC Mux", "IF_ADC1", "IF_ADC1" },
- { "IF1 ADC Mux", "IF_ADC2", "IF_ADC2" },
- { "IF1 ADC Mux", "VAD_ADC", "VAD_ADC" },
-
{ "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" },
- { "IF1 ADC", NULL, "IF1 ADC Mux" },
{ "IF2 ADC", NULL, "I2S2" },
{ "IF2 ADC", NULL, "IF2 ADC Mux" },
- { "AIF1TX", NULL, "IF1 ADC" },
- { "AIF1TX", NULL, "IF2 ADC" },
{ "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" },
- { "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" },
@@ -1893,14 +2469,12 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
{ "DAC1 MIXR", NULL, "dac stereo1 filter" },
- { "DAC L2 Mux", "IF1 DAC", "IF1 DAC2 L" },
{ "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", "IF1 DAC", "IF1 DAC2 R" },
{ "DAC R2 Mux", "IF2 DAC", "IF2 DAC R" },
{ "DAC R2 Mux", "Mono ADC", "Mono ADC MIXR" },
{ "DAC R2 Mux", "Haptic", "Haptic Generator" },
@@ -1981,9 +2555,7 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
{ "SPKVOL L", "Switch", "SPK MIXL" },
{ "SPKVOL R", "Switch", "SPK 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" },
{ "SPOR MIX", "DAC R1 Switch", "DAC R1" },
{ "SPOR MIX", "SPKVOL R Switch", "SPKVOL R" },
@@ -2038,6 +2610,80 @@ static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
{ "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[] = {
@@ -2045,27 +2691,93 @@ static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
{ "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_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, val_clk, mask_clk;
+ 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(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ 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);
@@ -2078,13 +2790,13 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
case 16:
break;
case 20:
- val_len |= RT5645_I2S_DL_20;
+ val_len = 0x1;
break;
case 24:
- val_len |= RT5645_I2S_DL_24;
+ val_len = 0x2;
break;
case 8:
- val_len |= RT5645_I2S_DL_8;
+ val_len = 0x3;
break;
default:
return -EINVAL;
@@ -2092,23 +2804,22 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
switch (dai->id) {
case RT5645_AIF1:
- mask_clk = RT5645_I2S_BCLK_MS1_MASK | RT5645_I2S_PD1_MASK;
- val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT |
- pre_div << RT5645_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
- RT5645_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ 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_update_bits(codec, RT5645_I2S2_SDP,
- RT5645_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk);
+ 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(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2117,15 +2828,24 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream,
static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- unsigned int reg_val = 0;
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5645->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5645_I2S_MS_S;
rt5645->master[dai->id] = 0;
break;
@@ -2137,7 +2857,7 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_NB_NF:
break;
case SND_SOC_DAIFMT_IB_NF:
- reg_val |= RT5645_I2S_BP_INV;
+ reg_val |= (1 << pol_sft);
break;
default:
return -EINVAL;
@@ -2160,17 +2880,17 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
switch (dai->id) {
case RT5645_AIF1:
- snd_soc_update_bits(codec, RT5645_I2S1_SDP,
- RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK |
+ 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_update_bits(codec, RT5645_I2S2_SDP,
- RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK |
+ 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(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -2179,8 +2899,8 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -2197,10 +2917,10 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5645_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_SCLK_SRC_MASK, reg_val);
rt5645->sysclk = freq;
rt5645->sysclk_src = clk_id;
@@ -2213,8 +2933,8 @@ static int rt5645_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -2223,56 +2943,56 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5645->pll_in = 0;
rt5645->pll_out = 0;
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ 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_update_bits(codec, RT5645_GLB_CLK,
+ 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_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK1);
break;
case RT5645_AIF2:
- snd_soc_update_bits(codec, RT5645_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5645_GLB_CLK,
RT5645_PLL1_SRC_MASK, RT5645_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5645_PLL_CTRL1,
+ snd_soc_component_write(component, RT5645_PLL_CTRL1,
pll_code.n_code << RT5645_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, 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);
+ 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;
@@ -2284,24 +3004,43 @@ static int rt5645_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
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_codec *codec = dai->codec;
- unsigned int val = 0;
+ 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 << 14);
- snd_soc_update_bits(codec, RT5645_BASS_BACK,
- RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
+ 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 << 12);
+ val |= (1 << i_slot_sft) | (1 << o_slot_sft);
break;
case 6:
- val |= (2 << 12);
+ val |= (2 << i_slot_sft) | (2 << o_slot_sft);
break;
case 8:
- val |= (3 << 12);
+ val |= (3 << i_slot_sft) | (3 << o_slot_sft);
break;
case 2:
default:
@@ -2310,59 +3049,72 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
switch (slot_width) {
case 20:
- val |= (1 << 10);
+ val |= (1 << i_width_sht) | (1 << o_width_sht);
break;
case 24:
- val |= (2 << 10);
+ val |= (2 << i_width_sht) | (2 << o_width_sht);
break;
case 32:
- val |= (3 << 10);
+ val |= (3 << i_width_sht) | (3 << o_width_sht);
break;
case 16:
default:
break;
}
- snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, 0x7c00, val);
+ snd_soc_component_update_bits(component, RT5645_TDM_CTRL_1, mask, val);
return 0;
}
-static int rt5645_set_bias_level(struct snd_soc_codec *codec,
+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 == codec->dapm.bias_level) {
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ 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_update_bits(codec, RT5645_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
RT5645_PWR_FV1 | RT5645_PWR_FV2,
RT5645_PWR_FV1 | RT5645_PWR_FV2);
- snd_soc_update_bits(codec, RT5645_GEN_CTRL1,
+ 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_update_bits(codec, RT5645_PWR_ANLG1,
+ 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);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ 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_write(codec, RT5645_DEPOP_M2, 0x1100);
- snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
- snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+ 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);
@@ -2371,83 +3123,309 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int rt5645_jack_detect(struct snd_soc_codec *codec)
+static void rt5645_enable_push_button_irq(struct snd_soc_component *component,
+ bool enable)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
- int gpio_state, jack_type = 0;
- unsigned int val;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
- if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
- dev_err(codec->dev, "invalid gpio\n");
- return -EINVAL;
- }
- gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
+ 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_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_RST);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD2,
+ RT5645_EN_4BTN_IL_MASK | RT5645_RST_4BTN_IL_MASK,
+ RT5645_EN_4BTN_IL_EN | RT5645_RST_4BTN_IL_NORM);
+ msleep(50);
+ ret = snd_soc_component_read(component, RT5645_INT_IRQ_ST);
+ pr_debug("%s read %x = %x\n", __func__, RT5645_INT_IRQ_ST,
+ snd_soc_component_read(component, RT5645_INT_IRQ_ST));
+ snd_soc_component_write(component, RT5645_INT_IRQ_ST, ret);
+ ret = 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));
+ snd_soc_component_write(component, RT5650_4BTN_IL_CMD1, ret);
+ snd_soc_component_update_bits(component, RT5650_4BTN_IL_CMD1, 0x3, 0x3);
+ snd_soc_component_update_bits(component,
+ RT5645_INT_IRQ_ST, 0x8, 0x8);
+ } 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);
- dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
- gpio_state);
+ snd_soc_dapm_disable_pin(dapm, "ADC L power");
+ snd_soc_dapm_disable_pin(dapm, "ADC R power");
+ snd_soc_dapm_sync(dapm);
+ }
+}
- if ((rt5645->pdata.gpio_hp_det_active_high && gpio_state) ||
- (!rt5645->pdata.gpio_hp_det_active_high && !gpio_state)) {
- snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias1");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "micbias2");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "Mic Det Power");
- snd_soc_dapm_sync(&codec->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;
- snd_soc_write(codec, RT5645_IN1_CTRL1, 0x0006);
- snd_soc_write(codec, RT5645_JD_CTRL3, 0x00b0);
+ 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);
+ }
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
- RT5645_CBJ_MN_JD, 0);
- snd_soc_update_bits(codec, RT5645_IN1_CTRL2,
+ 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(400);
- val = snd_soc_read(codec, RT5645_IN1_CTRL3) & 0x7;
- dev_dbg(codec->dev, "val = %d\n", val);
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 1);
- if (val == 1 || val == 2)
- jack_type = SND_JACK_HEADSET;
- else
- jack_type = SND_JACK_HEADPHONE;
+ 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->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
+ }
+ 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);
- snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
- snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
if (rt5645->pdata.jd_mode == 0)
- snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
- snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
- snd_soc_dapm_sync(&codec->dapm);
+ 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);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
}
- snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
- snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
- return 0;
+ return rt5645->jack_type;
}
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
+static int rt5645_button_detect(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ 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_jack_detect(codec);
+ 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);
+ mutex_unlock(&rt5645->jd_mutex);
+ 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);
- rt5645_jack_detect(rt5645->codec);
+ regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+ RT5645_PWR_CLK25M_MASK, RT5645_PWR_CLK25M_PD);
}
static irqreturn_t rt5645_irq(int irq, void *data)
@@ -2460,52 +3438,77 @@ static irqreturn_t rt5645_irq(int irq, void *data)
return IRQ_HANDLED;
}
-static int rt5645_probe(struct snd_soc_codec *codec)
+static void rt5645_btn_check_callback(struct timer_list *t)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = timer_container_of(rt5645, t,
+ btn_check_timer);
+
+ queue_delayed_work(system_power_efficient_wq,
+ &rt5645->jack_detect_work, msecs_to_jiffies(5));
+}
- rt5645->codec = codec;
+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_add_routes(&codec->dapm,
+ 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(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5650_specific_dapm_widgets,
ARRAY_SIZE(rt5650_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5650_specific_dapm_routes,
ARRAY_SIZE(rt5650_specific_dapm_routes));
break;
}
- rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
/* for JD function */
- if (rt5645->pdata.en_jd_func) {
- snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power");
- snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
- snd_soc_dapm_sync(&codec->dapm);
+ 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 int rt5645_remove(struct snd_soc_codec *codec)
+static void rt5645_remove(struct snd_soc_component *component)
{
- rt5645_reset(codec);
- return 0;
+ rt5645_reset(component);
}
#ifdef CONFIG_PM
-static int rt5645_suspend(struct snd_soc_codec *codec)
+static int rt5645_suspend(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, true);
regcache_mark_dirty(rt5645->regmap);
@@ -2513,9 +3516,9 @@ static int rt5645_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int rt5645_resume(struct snd_soc_codec *codec)
+static int rt5645_resume(struct snd_soc_component *component)
{
- struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5645->regmap, false);
regcache_sync(rt5645->regmap);
@@ -2531,7 +3534,7 @@ static int rt5645_resume(struct snd_soc_codec *codec)
#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5645_aif_dai_ops = {
+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,
@@ -2553,7 +3556,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
- .channels_max = 2,
+ .channels_max = 4,
.rates = RT5645_STEREO_RATES,
.formats = RT5645_FORMATS,
},
@@ -2580,51 +3583,424 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
- .probe = rt5645_probe,
- .remove = rt5645_remove,
- .suspend = rt5645_suspend,
- .resume = rt5645_resume,
- .set_bias_level = rt5645_set_bias_level,
- .idle_bias_off = true,
- .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),
+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_RBTREE,
+ .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 },
+ { "rt5645" },
+ { "rt5650" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
-static int rt5645_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *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[] = {
+ { "10EC3270" },
+ { "10EC5640" },
+ { "10EC5645" },
+ { "10EC5648" },
+ { "10EC5650" },
+ { }
+};
+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,
+ .mono_speaker = 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_monospk_platform_data = {
+ .jd_mode = 3,
+ .mono_speaker = true,
+};
+
+static const struct rt5645_platform_data jd_mode3_inv_data = {
+ .jd_mode = 3,
+ .inv_jd1_1 = 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_monospk_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"),
+ /*
+ * Above strings are too generic, LattePanda BIOS versions for
+ * all 4 hw revisions are:
+ * DF-BI-7-S70CR100-*
+ * DF-BI-7-S70CR110-*
+ * DF-BI-7-S70CR200-*
+ * LP-BS-7-S70CR700-*
+ * Do a partial match for S70CR to avoid false positive matches.
+ */
+ DMI_MATCH(DMI_BIOS_VERSION, "S70CR"),
+ },
+ .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,
+ },
+ {
+ .ident = "Acer Switch V 10 (SW5-017)",
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
+ },
+ .driver_data = (void *)&intel_braswell_platform_data,
+ },
+ {
+ .ident = "Meegopad T08",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Default string"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Default string"),
+ DMI_MATCH(DMI_BOARD_NAME, "T3 MRD"),
+ DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
+ },
+ .driver_data = (void *)&jd_mode3_inv_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 void rt5645_parse_dt(struct device *dev, struct rt5645_platform_data *pdata)
+{
+ pdata->in2_diff = device_property_read_bool(dev, "realtek,in2-differential");
+ device_property_read_u32(dev, "realtek,dmic1-data-pin", &pdata->dmic1_data_pin);
+ device_property_read_u32(dev, "realtek,dmic2-data-pin", &pdata->dmic2_data_pin);
+ device_property_read_u32(dev, "realtek,jd-mode", &pdata->jd_mode);
+}
+
+static void rt5645_get_pdata(struct device *codec_dev, struct rt5645_platform_data *pdata)
+{
+ const struct dmi_system_id *dmi_data;
+
+ dmi_data = dmi_first_match(dmi_platform_data);
+ if (dmi_data) {
+ dev_info(codec_dev, "Detected %s platform\n", dmi_data->ident);
+ *pdata = *((struct rt5645_platform_data *)dmi_data->driver_data);
+ } else if (rt5645_check_dp(codec_dev)) {
+ rt5645_parse_dt(codec_dev, pdata);
+ } else {
+ *pdata = jd_mode3_platform_data;
+ }
+
+ if (quirk != -1) {
+ pdata->in2_diff = QUIRK_IN2_DIFF(quirk);
+ pdata->level_trigger_irq = QUIRK_LEVEL_IRQ(quirk);
+ pdata->inv_jd1_1 = QUIRK_INV_JD1_1(quirk);
+ pdata->inv_hp_pol = QUIRK_INV_HP_POL(quirk);
+ pdata->jd_mode = QUIRK_JD_MODE(quirk);
+ pdata->dmic1_data_pin = QUIRK_DMIC1_DATA_PIN(quirk);
+ pdata->dmic2_data_pin = QUIRK_DMIC2_DATA_PIN(quirk);
+ }
+}
+
+const char *rt5645_components(struct device *codec_dev)
+{
+ struct rt5645_platform_data pdata = { };
+ static char buf[32];
+ const char *mic;
+ int spk = 2;
+
+ rt5645_get_pdata(codec_dev, &pdata);
+
+ if (pdata.mono_speaker)
+ spk = 1;
+
+ if (pdata.dmic1_data_pin && pdata.dmic2_data_pin)
+ mic = "dmics12";
+ else if (pdata.dmic1_data_pin)
+ mic = "dmic1";
+ else if (pdata.dmic2_data_pin)
+ mic = "dmic2";
+ else
+ mic = "in2";
+
+ snprintf(buf, sizeof(buf), "cfg-spk:%d cfg-mic:%s", spk, mic);
+
+ return buf;
+}
+EXPORT_SYMBOL_GPL(rt5645_components);
+
+static int rt5645_i2c_probe(struct i2c_client *i2c)
{
- struct rt5645_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5645_priv *rt5645;
- int ret;
+ int ret, i;
unsigned int val;
+ struct regmap *regmap;
rt5645 = devm_kzalloc(&i2c->dev, sizeof(struct rt5645_priv),
GFP_KERNEL);
@@ -2633,131 +4009,200 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
rt5645->i2c = i2c;
i2c_set_clientdata(i2c, rt5645);
+ rt5645_get_pdata(&i2c->dev, &rt5645->pdata);
- if (pdata)
- rt5645->pdata = *pdata;
+ 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");
+ }
- rt5645->regmap = devm_regmap_init_i2c(i2c, &rt5645_regmap);
- if (IS_ERR(rt5645->regmap)) {
- ret = PTR_ERR(rt5645->regmap);
- dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
- ret);
+ /* 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;
+ }
+
+ rt5645->gpiod_cbj_sleeve = devm_gpiod_get_optional(&i2c->dev, "cbj-sleeve",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(rt5645->gpiod_cbj_sleeve)) {
+ ret = PTR_ERR(rt5645->gpiod_cbj_sleeve);
+ dev_info(&i2c->dev, "failed to initialize gpiod, ret=%d\n", ret);
+ 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;
}
- regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
+ 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",
+ "Device with ID register %#x is not rt5645 or rt5650\n",
val);
- return -ENODEV;
+ 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);
- ret = regmap_register_patch(rt5645->regmap, init_list,
+ 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_register_patch(rt5645->regmap, rt5650_init_list,
+ 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.dmic_en) {
+ 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;
- 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_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;
+ 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;
- default:
- 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;
- 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;
+ default:
+ 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;
+ 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_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_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_GPIO12:
- regmap_update_bits(rt5645->regmap, RT5645_DMIC_CTRL1,
- RT5645_DMIC_1_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;
+ 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;
- default:
- 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.en_jd_func) {
+ if (rt5645->pdata.jd_mode) {
regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
- RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
- RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
- regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
- RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
- regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
- RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
- RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
+ 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);
- }
-
- if (rt5645->pdata.jd_mode) {
+ 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,
@@ -2780,6 +4225,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
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);
@@ -2787,56 +4233,130 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
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);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ goto err_enable;
+ }
}
- if (gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
- ret = gpio_request(rt5645->pdata.hp_det_gpio, "rt5645");
- if (ret)
- dev_err(&i2c->dev, "Fail gpio_request hp_det_gpio\n");
-
- ret = gpio_direction_input(rt5645->pdata.hp_det_gpio);
- if (ret)
- dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
- }
+ ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5645,
+ rt5645_dai, ARRAY_SIZE(rt5645_dai));
+ if (ret)
+ goto err_irq;
- INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+ return 0;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
- rt5645_dai, ARRAY_SIZE(rt5645_dai));
+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 int rt5645_i2c_remove(struct i2c_client *i2c)
+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
+ */
+ timer_delete_sync(&rt5645->btn_check_timer);
+
+ cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
+
+ 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);
+
+ if (rt5645->gpiod_cbj_sleeve)
+ gpiod_set_value(rt5645->gpiod_cbj_sleeve, 0);
+}
+
+static int rt5645_sys_suspend(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
+
+ timer_delete_sync(&rt5645->btn_check_timer);
cancel_delayed_work_sync(&rt5645->jack_detect_work);
+ cancel_delayed_work_sync(&rt5645->rcclock_work);
- if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
- gpio_free(rt5645->pdata.hp_det_gpio);
+ regcache_cache_only(rt5645->regmap, true);
+ regcache_mark_dirty(rt5645->regmap);
+ return 0;
+}
+
+static int rt5645_sys_resume(struct device *dev)
+{
+ struct rt5645_priv *rt5645 = dev_get_drvdata(dev);
- snd_soc_unregister_codec(&i2c->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 = {
+ SYSTEM_SLEEP_PM_OPS(rt5645_sys_suspend, rt5645_sys_resume)
+};
+
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rt5645_of_match),
+ .acpi_match_table = ACPI_PTR(rt5645_acpi_match),
+ .pm = pm_ptr(&rt5645_pm),
},
.probe = rt5645_i2c_probe,
- .remove = rt5645_i2c_remove,
+ .remove = rt5645_i2c_remove,
+ .shutdown = rt5645_i2c_shutdown,
.id_table = rt5645_i2c_id,
};
module_i2c_driver(rt5645_i2c_driver);
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index dbfd98c22f4d..bef74b29fd54 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5645.h -- RT5645 ALSA SoC audio driver
*
* Copyright 2013 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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 __RT5645_H__
#define __RT5645_H__
-#include <sound/rt5645.h>
-
/* Info */
#define RT5645_RESET 0x00
#define RT5645_VENDOR_ID 0xfd
@@ -39,8 +34,8 @@
#define RT5645_STO1_ADC_DIG_VOL 0x1c
#define RT5645_MONO_ADC_DIG_VOL 0x1d
#define RT5645_ADC_BST_VOL1 0x1e
-/* Mixer - D-D */
#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
@@ -105,6 +100,7 @@
#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
@@ -314,12 +310,14 @@
#define RT5645_STO1_ADC_R_BST_SFT 12
#define RT5645_STO1_ADC_COMP_MASK (0x3 << 10)
#define RT5645_STO1_ADC_COMP_SFT 10
-#define RT5645_STO2_ADC_L_BST_MASK (0x3 << 8)
-#define RT5645_STO2_ADC_L_BST_SFT 8
-#define RT5645_STO2_ADC_R_BST_MASK (0x3 << 6)
-#define RT5645_STO2_ADC_R_BST_SFT 6
-#define RT5645_STO2_ADC_COMP_MASK (0x3 << 4)
-#define RT5645_STO2_ADC_COMP_SFT 4
+
+/* 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)
@@ -618,14 +616,14 @@
#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_L1_SM_L (0x1 << 1)
-#define RT5645_M_DAC_L1_SM_L_SFT 1
#define RT5645_M_DAC_L2_SM_L (0x1 << 2)
#define RT5645_M_DAC_L2_SM_L_SFT 2
-#define RT5645_M_BST3_L_SM_L (0x1 << 4)
-#define RT5645_M_BST3_L_SM_L_SFT 4
+#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)
@@ -640,14 +638,14 @@
#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_R1_SM_R (0x1 << 1)
-#define RT5645_M_DAC_R1_SM_R_SFT 1
#define RT5645_M_DAC_R2_SM_R (0x1 << 2)
#define RT5645_M_DAC_R2_SM_R_SFT 2
-#define RT5645_M_BST3_R_SM_R (0x1 << 4)
-#define RT5645_M_BST3_R_SM_R_SFT 4
+#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)
@@ -667,13 +665,17 @@
#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_G_MONOMIX_MASK (0x1 << 10)
-#define RT5645_G_MONOMIX_SFT 10
#define RT5645_M_BST2_MM (0x1 << 4)
#define RT5645_M_BST2_MM_SFT 4
#define RT5645_M_DAC_R1_MM (0x1 << 3)
@@ -776,8 +778,6 @@
#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_ADC_R (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT 1
#define RT5645_PWR_DAC_L2 (0x1 << 7)
#define RT5645_PWR_DAC_L2_BIT 7
#define RT5645_PWR_DAC_R2 (0x1 << 6)
@@ -804,8 +804,6 @@
#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_ADC_S2F (0x1 << 8)
-#define RT5645_PWR_ADC_S2F_BIT 8
#define RT5645_PWR_PDM1 (0x1 << 7)
#define RT5645_PWR_PDM1_BIT 7
#define RT5645_PWR_PDM2 (0x1 << 6)
@@ -944,10 +942,6 @@
#define RT5645_I2S2_SDI_I2S2 (0x1 << 6)
/* ADC/DAC Clock Control 1 (0x73) */
-#define RT5645_I2S_BCLK_MS1_MASK (0x1 << 15)
-#define RT5645_I2S_BCLK_MS1_SFT 15
-#define RT5645_I2S_BCLK_MS1_32 (0x0 << 15)
-#define RT5645_I2S_BCLK_MS1_64 (0x1 << 15)
#define RT5645_I2S_PD1_MASK (0x7 << 12)
#define RT5645_I2S_PD1_SFT 12
#define RT5645_I2S_PD1_1 (0x0 << 12)
@@ -1069,13 +1063,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) /* 15MHz */
-#define RT5645_PLL1_SRC_MASK (0x3 << 12)
-#define RT5645_PLL1_SRC_SFT 12
-#define RT5645_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5645_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5645_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5645_PLL1_SRC_BCLK3 (0x3 << 12)
+#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)
@@ -1630,6 +1625,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)
@@ -1697,6 +1696,10 @@
#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)
@@ -2008,8 +2011,17 @@
#define RT5645_ZCD_HP_DIS (0x0 << 15)
#define RT5645_ZCD_HP_EN (0x1 << 15)
+/* Buttons Inline Command Function 2 (0xe0) */
+#define RT5645_EN_4BTN_IL_MASK (0x1 << 15)
+#define RT5645_EN_4BTN_IL_EN (0x1 << 15)
+#define RT5645_RST_4BTN_IL_MASK (0x1 << 14)
+#define RT5645_RST_4BTN_IL_RST (0x0 << 14)
+#define RT5645_RST_4BTN_IL_NORM (0x1 << 14)
/* 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
@@ -2106,6 +2118,12 @@ enum {
#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)
@@ -2114,7 +2132,12 @@ enum {
/* 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
@@ -2149,6 +2172,7 @@ enum {
};
enum {
+ RT5645_DMIC1_DISABLE,
RT5645_DMIC_DATA_IN2P,
RT5645_DMIC_DATA_GPIO6,
RT5645_DMIC_DATA_GPIO10,
@@ -2156,6 +2180,7 @@ enum {
};
enum {
+ RT5645_DMIC2_DISABLE,
RT5645_DMIC_DATA_IN2N,
RT5645_DMIC_DATA_GPIO5,
RT5645_DMIC_DATA_GPIO11,
@@ -2176,31 +2201,13 @@ enum {
RT5645_AD_MONO_R_FILTER = (0x1 << 5),
};
-int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
+int rt5645_sel_asrc_clk_src(struct snd_soc_component *component,
unsigned int filter_mask, unsigned int clk_src);
-struct rt5645_priv {
- struct snd_soc_codec *codec;
- struct rt5645_platform_data pdata;
- struct regmap *regmap;
- struct i2c_client *i2c;
- struct snd_soc_jack *hp_jack;
- struct snd_soc_jack *mic_jack;
- struct delayed_work jack_detect_work;
-
- 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 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);
-int rt5645_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
+const char *rt5645_components(struct device *codec_dev);
#endif /* __RT5645_H__ */
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 9f4c7be6d798..9eeeba8cd6ff 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1,23 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5651.c -- RT5651 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
+#include <linux/acpi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -25,6 +23,7 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/jack.h>
#include "rl6231.h"
#include "rt5651.h"
@@ -46,7 +45,7 @@ static const struct regmap_range_cfg rt5651_ranges[] = {
.window_len = 0x1, },
};
-static struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5651_PR_BASE + 0x3d, 0x3e00},
};
@@ -286,22 +285,21 @@ static bool rt5651_readable_register(struct device *dev, unsigned int reg)
}
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_MINMAX(dac_vol_tlv, -6562, 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_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 unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5651_data_select[] = {
@@ -330,11 +328,13 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
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 Control */
+ /* 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,
@@ -376,35 +376,22 @@ static const struct snd_kcontrol_new rt5651_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5651->sysclk);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
+ snd_soc_component_update_bits(component, RT5651_DMIC, RT5651_DMIC_CLK_MASK,
idx << RT5651_DMIC_CLK_SFT);
return idx;
}
-static int is_sysclk_from_pll(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
-
- val = snd_soc_read(codec, RT5651_GLB_CLK);
- val &= RT5651_SCLK_SRC_MASK;
- if (val == RT5651_SCLK_SRC_PLL1)
- return 1;
- else
- return 0;
-}
-
/* Digital Mixer */
static const struct snd_kcontrol_new rt5651_sto1_adc_l_mix[] = {
SOC_DAPM_SINGLE("ADC1 Switch", RT5651_STO1_ADC_MIXER,
@@ -585,44 +572,6 @@ static const struct snd_kcontrol_new hpo_r_mute_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5651_HP_VOL,
RT5651_R_MUTE_SFT, 1, 1);
-/* INL/R source */
-static const char * const rt5651_inl_src[] = {"IN2P", "HPOVOLLP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inl_enum, RT5651_INL1_INR1_VOL,
- RT5651_INL_SEL_SFT, rt5651_inl_src);
-
-static const struct snd_kcontrol_new rt5651_inl1_mux =
- SOC_DAPM_ENUM("INL1 source", rt5651_inl_enum);
-
-static const char * const rt5651_inr1_src[] = {"IN2N", "HPOVOLRP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inr1_enum, RT5651_INL1_INR1_VOL,
- RT5651_INR_SEL_SFT, rt5651_inr1_src);
-
-static const struct snd_kcontrol_new rt5651_inr1_mux =
- SOC_DAPM_ENUM("INR1 source", rt5651_inr1_enum);
-
-static const char * const rt5651_inl2_src[] = {"IN3P", "OUTVOLLP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inl2_enum, RT5651_INL2_INR2_VOL,
- RT5651_INL_SEL_SFT, rt5651_inl2_src);
-
-static const struct snd_kcontrol_new rt5651_inl2_mux =
- SOC_DAPM_ENUM("INL2 source", rt5651_inl2_enum);
-
-static const char * const rt5651_inr2_src[] = {"IN3N", "OUTVOLRP"};
-
-static SOC_ENUM_SINGLE_DECL(
- rt5651_inr2_enum, RT5651_INL2_INR2_VOL,
- RT5651_INR_SEL_SFT, rt5651_inr2_src);
-
-static const struct snd_kcontrol_new rt5651_inr2_mux =
- SOC_DAPM_ENUM("INR2 source", rt5651_inr2_enum);
-
-
/* Stereo ADC source */
static const char * const rt5651_stereo1_adc1_src[] = {"DD MIX", "ADC"};
@@ -732,8 +681,8 @@ static const struct snd_kcontrol_new rt5651_pdm_r_mux =
static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -770,8 +719,8 @@ static int rt5651_amp_power_event(struct snd_soc_dapm_widget *w,
static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -796,11 +745,11 @@ static int rt5651_hp_event(struct snd_soc_dapm_widget *w,
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 = 0;
+ rt5651->hp_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- rt5651->hp_mute = 1;
+ rt5651->hp_mute = true;
usleep_range(70000, 75000);
break;
@@ -815,8 +764,8 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -835,16 +784,16 @@ static int rt5651_hp_post_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ 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_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST1_OP2, 0);
break;
@@ -858,16 +807,16 @@ static int rt5651_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ 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_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST2_OP2, 0);
break;
@@ -881,16 +830,16 @@ static int rt5651_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5651_bst3_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5651_PWR_ANLG2,
+ 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_update_bits(codec, RT5651_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG2,
RT5651_PWR_BST3_OP2, 0);
break;
@@ -914,14 +863,11 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY_S("ADC ASRC", 1, RT5651_PLL_MODE_2,
11, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("PLL1", RT5651_PWR_ANLG2,
- RT5651_PWR_PLL_BIT, 0, NULL, 0),
- /* Input Side */
/* micbias */
SND_SOC_DAPM_SUPPLY("LDO", RT5651_PWR_ANLG1,
RT5651_PWR_LDO_BIT, 0, NULL, 0),
- SND_SOC_DAPM_MICBIAS("micbias1", RT5651_PWR_ANLG2,
- RT5651_PWR_MB1_BIT, 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"),
@@ -954,11 +900,7 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
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),
- /* IN Mux */
- SND_SOC_DAPM_MUX("INL1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl1_mux),
- SND_SOC_DAPM_MUX("INR1 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr1_mux),
- SND_SOC_DAPM_MUX("INL2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inl2_mux),
- SND_SOC_DAPM_MUX("INR2 Mux", SND_SOC_NOPM, 0, 0, &rt5651_inr2_mux),
+
/* 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)),
@@ -1199,7 +1141,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"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, "PLL1", is_sysclk_from_pll},
{"Stereo1 Filter", NULL, "ADC ASRC"},
{"Stereo1 ADC MIXR", "ADC1 Switch", "Stereo1 ADC R1 Mux"},
@@ -1209,7 +1150,6 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"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, "PLL1", is_sysclk_from_pll},
{"Stereo2 Filter", NULL, "ADC ASRC"},
{"Stereo2 ADC MIXR", "ADC1 Switch", "Stereo2 ADC R1 Mux"},
@@ -1276,10 +1216,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
{"PDM R Mux", "DD MIX", "DAC MIXR"},
{"DAC L1", NULL, "Stereo DAC MIXL"},
- {"DAC L1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC L1", NULL, "DAC L1 Power"},
{"DAC R1", NULL, "Stereo DAC MIXR"},
- {"DAC R1", NULL, "PLL1", is_sysclk_from_pll},
{"DAC R1", NULL, "DAC R1 Power"},
{"DD MIXL", "DAC L1 Switch", "DAC MIXL"},
@@ -1343,8 +1281,8 @@ static const struct snd_soc_dapm_route rt5651_dapm_routes[] = {
static int rt5651_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1352,12 +1290,12 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
pre_div = rl6231_get_clk_info(rt5651->sysclk, rt5651->lrck[dai->id]);
if (pre_div < 0) {
- dev_err(codec->dev, "Unsupported clock setting\n");
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32 ? 1 : 0;
@@ -1388,19 +1326,19 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
case RT5651_AIF1:
mask_clk = RT5651_I2S_PD1_MASK;
val_clk = pre_div << RT5651_I2S_PD1_SFT;
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S1_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ 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_update_bits(codec, RT5651_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5651_I2S2_SDP,
RT5651_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5651_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5651_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -1409,15 +1347,15 @@ static int rt5651_hw_params(struct snd_pcm_substream *substream,
static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5651->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5651_I2S_MS_S;
rt5651->master[dai->id] = 0;
break;
@@ -1453,17 +1391,17 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5651_AIF1:
- snd_soc_update_bits(codec, RT5651_I2S1_SDP,
+ 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_update_bits(codec, RT5651_I2S2_SDP,
+ 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(codec->dev, "Wrong dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Wrong dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
@@ -1472,9 +1410,10 @@ static int rt5651_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1485,15 +1424,18 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
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(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ 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;
@@ -1506,8 +1448,8 @@ static int rt5651_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1516,48 +1458,48 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5651->pll_in = 0;
rt5651->pll_out = 0;
- snd_soc_update_bits(codec, RT5651_GLB_CLK,
+ 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_update_bits(codec, RT5651_GLB_CLK,
+ 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_update_bits(codec, RT5651_GLB_CLK,
+ 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_update_bits(codec, RT5651_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5651_GLB_CLK,
RT5651_PLL1_SRC_MASK, RT5651_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5651_PLL_CTRL1,
+ snd_soc_component_write(component, RT5651_PLL_CTRL1,
pll_code.n_code << RT5651_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, 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);
+ 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;
@@ -1566,86 +1508,585 @@ static int rt5651_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
}
-static int rt5651_set_bias_level(struct snd_soc_codec *codec,
+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 == codec->dapm.bias_level) {
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
+ 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_update_bits(codec, RT5651_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
RT5651_PWR_FV1 | RT5651_PWR_FV2,
RT5651_PWR_FV1 | RT5651_PWR_FV2);
- snd_soc_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_LDO_DVO_MASK,
- RT5651_PWR_LDO_DVO_1_2V);
- snd_soc_update_bits(codec, RT5651_D_MISC, 0x1, 0x1);
- if (snd_soc_read(codec, RT5651_PLL_MODE_1) & 0x9200)
- snd_soc_update_bits(codec, RT5651_D_MISC,
- 0xc00, 0xc00);
+ snd_soc_component_update_bits(component, RT5651_D_MISC, 0x1, 0x1);
}
break;
- case SND_SOC_BIAS_STANDBY:
- snd_soc_write(codec, RT5651_D_MISC, 0x0010);
- snd_soc_write(codec, RT5651_PWR_DIG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_DIG2, 0x0000);
- snd_soc_write(codec, RT5651_PWR_VOL, 0x0000);
- snd_soc_write(codec, RT5651_PWR_MIXER, 0x0000);
- snd_soc_write(codec, RT5651_PWR_ANLG1, 0x0000);
- snd_soc_write(codec, RT5651_PWR_ANLG2, 0x0000);
+ 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;
}
- codec->dapm.bias_level = level;
+
+ 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;
}
-static int rt5651_probe(struct snd_soc_codec *codec)
+/*
+ * 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_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
- rt5651->codec = codec;
+ rt5651->component = component;
- snd_soc_update_bits(codec, 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_update_bits(codec, RT5651_PWR_ANLG1,
- RT5651_PWR_FV1 | RT5651_PWR_FV2,
- RT5651_PWR_FV1 | RT5651_PWR_FV2);
+ snd_soc_component_update_bits(component, RT5651_PWR_ANLG1,
+ RT5651_PWR_LDO_DVO_MASK, RT5651_PWR_LDO_DVO_1_2V);
- rt5651_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ 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_codec *codec)
+static int rt5651_suspend(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int rt5651_resume(struct snd_soc_component *component)
{
- struct rt5651_priv *rt5651 = snd_soc_codec_get_drvdata(codec);
+ struct rt5651_priv *rt5651 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5651->regmap, false);
- snd_soc_cache_sync(codec);
+ snd_soc_component_cache_sync(component);
return 0;
}
@@ -1706,18 +2147,20 @@ static struct snd_soc_dai_driver rt5651_dai[] = {
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
- .probe = rt5651_probe,
- .suspend = rt5651_suspend,
- .resume = rt5651_resume,
- .set_bias_level = rt5651_set_bias_level,
- .idle_bias_off = true,
- .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),
+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 = {
@@ -1729,25 +2172,47 @@ static const struct regmap_config rt5651_regmap = {
.volatile_reg = rt5651_volatile_register,
.readable_reg = rt5651_readable_register,
- .cache_type = REGCACHE_RBTREE,
+ .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[] = {
+ { "10EC5640" },
+ { "10EC5651" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
+#endif
static const struct i2c_device_id rt5651_i2c_id[] = {
- { "rt5651", 0 },
+ { "rt5651" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
-static int rt5651_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *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_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5651_priv *rt5651;
int ret;
+ int err;
rt5651 = devm_kzalloc(&i2c->dev, sizeof(*rt5651),
GFP_KERNEL);
@@ -1756,9 +2221,6 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5651);
- if (pdata)
- rt5651->pdata = *pdata;
-
rt5651->regmap = devm_regmap_init_i2c(i2c, &rt5651_regmap);
if (IS_ERR(rt5651->regmap)) {
ret = PTR_ERR(rt5651->regmap);
@@ -1767,10 +2229,13 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt5651->regmap, RT5651_DEVICE_ID, &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);
+ "Device with ID register %#x is not rt5651\n", ret);
return -ENODEV;
}
@@ -1781,36 +2246,40 @@ static int rt5651_i2c_probe(struct i2c_client *i2c,
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- if (rt5651->pdata.in2_diff)
- regmap_update_bits(rt5651->regmap, RT5651_IN1_IN2,
- RT5651_IN_DF2, RT5651_IN_DF2);
+ rt5651->irq = i2c->irq;
+ rt5651->hp_mute = true;
- if (rt5651->pdata.dmic_en)
- regmap_update_bits(rt5651->regmap, RT5651_GPIO_CTRL1,
- RT5651_GP2_PIN_MASK, RT5651_GP2_PIN_DMIC1_SCL);
+ INIT_DELAYED_WORK(&rt5651->bp_work, rt5651_button_press_work);
+ INIT_WORK(&rt5651->jack_detect_work, rt5651_jack_detect_work);
- rt5651->hp_mute = 1;
+ /* 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 request IRQ %d: %d\n",
+ rt5651->irq, ret);
+ rt5651->irq = -ENXIO;
+ }
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5651,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5651,
rt5651_dai, ARRAY_SIZE(rt5651_dai));
return ret;
}
-static int rt5651_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
-}
-
static struct i2c_driver rt5651_i2c_driver = {
.driver = {
.name = "rt5651",
- .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(rt5651_acpi_match),
+ .of_match_table = of_match_ptr(rt5651_of_match),
},
.probe = rt5651_i2c_probe,
- .remove = rt5651_i2c_remove,
.id_table = rt5651_i2c_id,
};
module_i2c_driver(rt5651_i2c_driver);
diff --git a/sound/soc/codecs/rt5651.h b/sound/soc/codecs/rt5651.h
index 1bd33cfa6411..20c33a3ece37 100644
--- a/sound/soc/codecs/rt5651.h
+++ b/sound/soc/codecs/rt5651.h
@@ -1,18 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5651.h -- RT5651 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
- *
- * 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 __RT5651_H__
#define __RT5651_H__
-#include <sound/rt5651.h>
+#include <dt-bindings/sound/rt5651.h>
/* Info */
#define RT5651_RESET 0x00
@@ -138,6 +135,7 @@
/* 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
@@ -1966,6 +1964,15 @@
#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
@@ -2059,10 +2066,25 @@ struct rt5651_pll_code {
};
struct rt5651_priv {
- struct snd_soc_codec *codec;
- struct rt5651_platform_data pdata;
+ 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];
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
new file mode 100644
index 000000000000..31b47db7b4f7
--- /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 <bardliao@realtek.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5659.h>
+
+#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_CBP_CFP:
+ rt5659->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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" },
+ { "rt5659" },
+ { }
+};
+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 request 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" },
+ { "10EC5659" },
+ { }
+};
+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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
new file mode 100644
index 000000000000..b49fd8baf4e7
--- /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 <bardliao@realtek.com>
+ */
+
+#ifndef __RT5659_H__
+#define __RT5659_H__
+
+#include <sound/rt5659.h>
+
+#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 000000000000..44c3a3b92f98
--- /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 <oder_chiou@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ rt5660->master[dai->id] = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 (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" },
+ { }
+};
+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[] = {
+ { "10EC3277" },
+ { "10EC5660" },
+ { }
+};
+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_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5660->mclk))
+ return PTR_ERR(rt5660->mclk);
+
+ 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);
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP2_PIN_MASK, RT5660_GP2_PIN_DMIC1_SDA);
+ } 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 <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5660.h b/sound/soc/codecs/rt5660.h
new file mode 100644
index 000000000000..a33025c920e1
--- /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 <oder_chiou@realtek.com>
+ */
+
+#ifndef _RT5660_H
+#define _RT5660_H
+
+#include <linux/clk.h>
+#include <sound/rt5660.h>
+
+/* 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 000000000000..45057562c0c8
--- /dev/null
+++ b/sound/soc/codecs/rt5663.c
@@ -0,0 +1,3756 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5663.c -- RT5663 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Jack Yu <jack.yu@realtek.com>
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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;
+ unsigned int irq;
+};
+
+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_CBP_CFP:
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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);
+
+ if (rt5663->irq)
+ disable_irq(rt5663->irq);
+
+ cancel_delayed_work_sync(&rt5663->jack_detect_work);
+ cancel_delayed_work_sync(&rt5663->jd_unplug_work);
+
+ 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);
+
+ if (rt5663->irq)
+ enable_irq(rt5663->irq);
+
+ 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" },
+ {}
+};
+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" },
+ { }
+};
+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 request IRQ: %d\n",
+ __func__, ret);
+ goto err_enable;
+ }
+ rt5663->irq = i2c->irq;
+ }
+
+ 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 <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5663.h b/sound/soc/codecs/rt5663.h
new file mode 100644
index 000000000000..2c485d0655b5
--- /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 <jack.yu@realtek.com>
+ */
+
+#ifndef __RT5663_H__
+#define __RT5663_H__
+
+#include <sound/rt5663.h>
+
+/* 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 000000000000..b16b2c66e754
--- /dev/null
+++ b/sound/soc/codecs/rt5665.c
@@ -0,0 +1,4868 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5665.c -- RT5665/RT5658 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5665.h>
+
+#include "rl6231.h"
+#include "rt5665.h"
+
+static const char * const rt5665_supply_names[] = {
+ "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 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;
+}
+
+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_CBP_CFP:
+ rt5665->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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);
+}
+
+#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"},
+ {}
+};
+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 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);
+
+ ret = devm_regulator_bulk_get_enable(&i2c->dev, ARRAY_SIZE(rt5665_supply_names),
+ rt5665_supply_names);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request 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 request 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" },
+ { "10EC5666" },
+ { }
+};
+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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
new file mode 100644
index 000000000000..089e4078d37a
--- /dev/null
+++ b/sound/soc/codecs/rt5665.h
@@ -0,0 +1,2002 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5665.h -- RT5665/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5665_H__
+#define __RT5665_H__
+
+#include <sound/rt5665.h>
+
+#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,
+};
+
+#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
new file mode 100644
index 000000000000..8442dd09cfaf
--- /dev/null
+++ b/sound/soc/codecs/rt5668.c
@@ -0,0 +1,2585 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * rt5668.c -- RT5668B ALSA SoC audio component driver
+ *
+ * Copyright 2018 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5668.h>
+
+#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);
+}
+
+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_CBP_CFP:
+ rt5668->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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"},
+ {}
+};
+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 request 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" },
+ { }
+};
+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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h
new file mode 100644
index 000000000000..b34a61d2109c
--- /dev/null
+++ b/sound/soc/codecs/rt5668.h
@@ -0,0 +1,1312 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5668.h -- RT5668/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5668_H__
+#define __RT5668_H__
+
+#include <sound/rt5668.h>
+
+#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,
+};
+
+#endif /* __RT5668_H__ */
diff --git a/sound/soc/codecs/rt5670-dsp.h b/sound/soc/codecs/rt5670-dsp.h
index a34d0cdb8198..a07b7dfcf501 100644
--- a/sound/soc/codecs/rt5670-dsp.h
+++ b/sound/soc/codecs/rt5670-dsp.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670-dsp.h -- RT5670 ALSA SoC DSP driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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 __RT5670_DSP_H__
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index e1a4a45c57e2..efd26082f19a 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5670.c -- RT5670 ALSA SoC audio codec driver
*
* Copyright 2014 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -28,12 +25,30 @@
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/rt5670.h>
#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)
@@ -51,12 +66,12 @@ static const struct regmap_range_cfg rt5670_ranges[] = {
.window_len = 0x1, },
};
-static struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
- { RT5670_PR_BASE + 0x38, 0x3ba1 },
+ { RT5670_PR_BASE + 0x38, 0x1fe1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
+ { 0x8a, 0x0123 },
};
-#define RT5670_INIT_REG_LEN ARRAY_SIZE(init_list)
static const struct reg_default rt5670_reg[] = {
{ 0x00, 0x0000 },
@@ -132,7 +147,7 @@ static const struct reg_default rt5670_reg[] = {
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0123 },
{ 0x8b, 0x0000 },
{ 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
@@ -225,7 +240,6 @@ static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
case RT5670_ADC_EQ_CTRL1:
case RT5670_EQ_CTRL1:
case RT5670_ALC_CTRL_1:
- case RT5670_IRQ_CTRL1:
case RT5670_IRQ_CTRL2:
case RT5670_INT_IRQ_ST:
case RT5670_IL_CMD:
@@ -404,23 +418,205 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg)
}
}
+/**
+ * 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_SCALE(dac_vol_tlv, -65625, 375, 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_SCALE(adc_vol_tlv, -17625, 375, 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 unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
+);
/* Interface data select */
static const char * const rt5670_data_select[] = {
@@ -433,21 +629,69 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_dac_enum, RT5670_DIG_INF1_DATA,
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("HP Playback Switch", RT5670_HP_VOL,
- RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
- 39, 0, out_vol_tlv),
+ 39, 1, out_vol_tlv),
/* OUTPUT Control */
- SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1,
- RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1),
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),
@@ -500,16 +744,17 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
- int idx = -EINVAL;
-
- idx = rl6231_calc_dmic_clk(rt5670->sysclk);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ dev_err(component->dev, "Failed to set DMIC clock\n");
else
- snd_soc_update_bits(codec, RT5670_DMIC_CTRL1,
+ snd_soc_component_update_bits(component, RT5670_DMIC_CTRL1,
RT5670_DMIC_CLK_MASK, idx << RT5670_DMIC_CLK_SFT);
return idx;
}
@@ -517,12 +762,10 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- unsigned int val;
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
- val = snd_soc_read(codec, RT5670_GLB_CLK);
- val &= RT5670_SCLK_SRC_MASK;
- if (val == RT5670_SCLK_SRC_PLL1)
+ if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1)
return 1;
else
return 0;
@@ -531,7 +774,7 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm);
unsigned int reg, shift, val;
switch (source->shift) {
@@ -567,7 +810,7 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
return 0;
}
- val = (snd_soc_read(codec, reg) >> shift) & 0xf;
+ val = (snd_soc_component_read(component, reg) >> shift) & 0xf;
switch (val) {
case 1:
case 2:
@@ -583,8 +826,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -595,7 +838,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
/**
* rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @codec: SoC audio codec device.
+ * @component: SoC audio component device.
* @filter_mask: mask of filters.
* @clk_src: clock source
*
@@ -607,7 +850,7 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
* 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_codec *codec,
+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;
@@ -665,11 +908,11 @@ int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
}
if (asrc2_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_2,
+ snd_soc_component_update_bits(component, RT5670_ASRC_2,
asrc2_mask, asrc2_value);
if (asrc3_mask)
- snd_soc_update_bits(codec, RT5670_ASRC_3,
+ snd_soc_component_update_bits(component, RT5670_ASRC_3,
asrc3_mask, asrc3_value);
return 0;
}
@@ -718,18 +961,44 @@ static const struct snd_kcontrol_new rt5670_mono_adc_r_mix[] = {
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("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_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("DAC1 Switch", RT5670_AD_DA_MIXER,
- RT5670_M_DAC1_R_SFT, 1, 1),
+ SOC_DAPM_SINGLE_RT5670_DAC1_SW("DAC1 Switch", 1),
};
static const struct snd_kcontrol_new rt5670_sto_dac_l_mix[] = {
@@ -859,20 +1128,6 @@ static const struct snd_kcontrol_new rt5670_lout_mix[] = {
RT5670_M_OV_R_LM_SFT, 1, 1),
};
-static const struct snd_kcontrol_new rt5670_hpl_mix[] = {
- SOC_DAPM_SINGLE("DAC L1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACL1_HML_SFT, 1, 1),
- SOC_DAPM_SINGLE("INL1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INL1_HML_SFT, 1, 1),
-};
-
-static const struct snd_kcontrol_new rt5670_hpr_mix[] = {
- SOC_DAPM_SINGLE("DAC R1 Switch", RT5670_HPO_MIXER,
- RT5670_M_DACR1_HMR_SFT, 1, 1),
- SOC_DAPM_SINGLE("INR1 Switch", RT5670_HPO_MIXER,
- RT5670_M_INR1_HMR_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);
@@ -971,20 +1226,15 @@ static const char * const rt5670_stereo_adc1_src[] = {
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_l1_mux =
- SOC_DAPM_ENUM("Stereo1 ADC L1 source", rt5670_stereo1_adc1_enum);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_r1_mux =
- SOC_DAPM_ENUM("Stereo1 ADC R1 source", rt5670_stereo1_adc1_enum);
+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_l1_mux =
- SOC_DAPM_ENUM("Stereo2 ADC L1 source", rt5670_stereo2_adc1_enum);
+static const struct snd_kcontrol_new rt5670_sto2_adc_1_mux =
+ SOC_DAPM_ENUM("Stereo2 ADC 1 Mux", rt5670_stereo2_adc1_enum);
-static const struct snd_kcontrol_new rt5670_sto2_adc_r1_mux =
- SOC_DAPM_ENUM("Stereo2 ADC R1 source", rt5670_stereo2_adc1_enum);
/* MX-27 MX-26 [11] */
static const char * const rt5670_stereo_adc2_src[] = {
@@ -994,37 +1244,14 @@ static const char * const rt5670_stereo_adc2_src[] = {
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_l2_mux =
- SOC_DAPM_ENUM("Stereo1 ADC L2 source", rt5670_stereo1_adc2_enum);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_r2_mux =
- SOC_DAPM_ENUM("Stereo1 ADC R2 source", rt5670_stereo1_adc2_enum);
+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_l2_mux =
- SOC_DAPM_ENUM("Stereo2 ADC L2 source", rt5670_stereo2_adc2_enum);
-
-static const struct snd_kcontrol_new rt5670_sto2_adc_r2_mux =
- SOC_DAPM_ENUM("Stereo2 ADC R2 source", rt5670_stereo2_adc2_enum);
-
-/* MX-27 MX26 [10] */
-static const char * const rt5670_stereo_adc_src[] = {
- "ADC1L ADC2R", "ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo1_adc_enum, RT5670_STO1_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto_adc_mux =
- SOC_DAPM_ENUM("Stereo1 ADC source", rt5670_stereo1_adc_enum);
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_adc_enum, RT5670_STO2_ADC_MIXER,
- RT5670_ADC_SRC_SFT, rt5670_stereo_adc_src);
-
-static const struct snd_kcontrol_new rt5670_sto2_adc_mux =
- SOC_DAPM_ENUM("Stereo2 ADC source", rt5670_stereo2_adc_enum);
+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[] = {
@@ -1043,17 +1270,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_stereo2_dmic_enum, RT5670_STO2_ADC_MIXER,
static const struct snd_kcontrol_new rt5670_sto2_dmic_mux =
SOC_DAPM_ENUM("Stereo2 DMIC source", rt5670_stereo2_dmic_enum);
-/* MX-27 [0] */
-static const char * const rt5670_stereo_dmic3_src[] = {
- "DMIC3", "PDM ADC"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_stereo_dmic3_enum, RT5670_STO1_ADC_MIXER,
- RT5670_DMIC3_SRC_SFT, rt5670_stereo_dmic3_src);
-
-static const struct snd_kcontrol_new rt5670_sto_dmic3_mux =
- SOC_DAPM_ENUM("Stereo DMIC3 source", rt5670_stereo_dmic3_enum);
-
/* Mono ADC source */
/* MX-28 [12] */
static const char * const rt5670_mono_adc_l1_src[] = {
@@ -1146,17 +1362,6 @@ static SOC_ENUM_SINGLE_DECL(rt5670_if2_adc_in_enum, RT5670_DIG_INF1_DATA,
static const struct snd_kcontrol_new rt5670_if2_adc_in_mux =
SOC_DAPM_ENUM("IF2 ADC IN source", rt5670_if2_adc_in_enum);
-/* MX-30 [5:4] */
-static const char * const rt5670_if4_adc_in_src[] = {
- "IF_ADC1", "IF_ADC2", "IF_ADC3"
-};
-
-static SOC_ENUM_SINGLE_DECL(rt5670_if4_adc_in_enum, RT5670_DIG_INF2_DATA,
- RT5670_IF4_ADC_IN_SFT, rt5670_if4_adc_in_src);
-
-static const struct snd_kcontrol_new rt5670_if4_adc_in_mux =
- SOC_DAPM_ENUM("IF4 ADC IN source", rt5670_if4_adc_in_enum);
-
/* MX-31 [15] [13] [11] [9] */
static const char * const rt5670_pdm_src[] = {
"Mono DAC", "Stereo DAC"
@@ -1233,8 +1438,8 @@ static const struct snd_kcontrol_new rt5670_vad_adc_mux =
static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1269,8 +1474,8 @@ static int rt5670_hp_power_event(struct snd_soc_dapm_widget *w,
static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -1316,19 +1521,46 @@ static int rt5670_hp_event(struct snd_soc_dapm_widget *w,
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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ 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_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST1_P, 0);
break;
@@ -1342,16 +1574,16 @@ static int rt5670_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5670_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- snd_soc_update_bits(codec, RT5670_PWR_ANLG2,
+ 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_update_bits(codec, RT5670_PWR_ANLG2,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG2,
RT5670_PWR_BST2_P, 0);
break;
@@ -1462,23 +1694,23 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
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_l2_mux),
+ &rt5670_sto_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_r2_mux),
+ &rt5670_sto_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_l1_mux),
+ &rt5670_sto_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo1 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto_adc_r1_mux),
+ &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_l2_mux),
+ &rt5670_sto2_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC R2 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_r2_mux),
+ &rt5670_sto2_adc_2_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC L1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_l1_mux),
+ &rt5670_sto2_adc_1_mux),
SND_SOC_DAPM_MUX("Stereo2 ADC R1 Mux", SND_SOC_NOPM, 0, 0,
- &rt5670_sto2_adc_r1_mux),
+ &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,
@@ -1498,12 +1730,10 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
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", RT5670_STO1_ADC_DIG_VOL,
- RT5670_L_MUTE_SFT, 1, rt5670_sto1_adc_l_mix,
- ARRAY_SIZE(rt5670_sto1_adc_l_mix)),
- SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", RT5670_STO1_ADC_DIG_VOL,
- RT5670_R_MUTE_SFT, 1, rt5670_sto1_adc_r_mix,
- ARRAY_SIZE(rt5670_sto1_adc_r_mix)),
+ 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)),
@@ -1537,7 +1767,6 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
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),
/* DSP */
SND_SOC_DAPM_PGA("TxDP_ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1730,7 +1959,9 @@ static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
- SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+ 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"),
@@ -1843,7 +2074,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "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" },
@@ -1882,7 +2112,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "Stereo2 ADC MIXL", NULL, "Stereo2 ADC LR Mux" },
{ "Stereo2 ADC MIXL", NULL, "ADC Stereo2 Filter" },
- { "ADC Stereo2 Filter", NULL, "PLL1", is_sys_clk_from_pll },
{ "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
{ "Stereo2 ADC MIXR", NULL, "ADC Stereo2 Filter" },
@@ -1906,13 +2135,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "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", "IF1_ADC4" },
+ { "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", "IF1_ADC4" },
+ { "IF1 ADC2 IN1 Mux", "IF1_ADC4", "TxDP_ADC" },
{ "IF1_ADC1" , NULL, "IF1 ADC1 IN2 Mux" },
{ "IF1_ADC2" , NULL, "IF1 ADC2 IN1 Mux" },
@@ -2137,21 +2366,21 @@ static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
static int rt5670_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Unsupported clock setting %d for DAI %d\n",
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -2183,20 +2412,20 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
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_update_bits(codec, RT5670_I2S1_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S1_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ 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_update_bits(codec, RT5670_I2S2_SDP,
+ snd_soc_component_update_bits(component, RT5670_I2S2_SDP,
RT5670_I2S_DL_MASK, val_len);
- snd_soc_update_bits(codec, RT5670_ADDA_CLK1, mask_clk, val_clk);
+ snd_soc_component_update_bits(component, RT5670_ADDA_CLK1, mask_clk, val_clk);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
@@ -2205,15 +2434,15 @@ static int rt5670_hw_params(struct snd_pcm_substream *substream,
static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5670->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5670_I2S_MS_S;
rt5670->master[dai->id] = 0;
break;
@@ -2249,39 +2478,28 @@ static int rt5670_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_I2S1_SDP,
+ 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_update_bits(codec, RT5670_I2S2_SDP,
+ 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(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
return 0;
}
-static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+static int rt5670_set_codec_sysclk(struct snd_soc_component *component, int clk_id,
+ int source, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
unsigned int reg_val = 0;
- if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src)
- return 0;
-
- if (rt5670->pdata.jd_mode) {
- if (clk_id == RT5670_SCLK_S_PLL1)
- snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1");
- else
- snd_soc_dapm_disable_pin(&codec->dapm, "PLL1");
- snd_soc_dapm_sync(&codec->dapm);
- }
switch (clk_id) {
case RT5670_SCLK_S_MCLK:
reg_val |= RT5670_SCLK_SRC_MCLK;
@@ -2293,15 +2511,16 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5670_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, reg_val);
rt5670->sysclk = freq;
- rt5670->sysclk_src = clk_id;
+ if (clk_id != RT5670_SCLK_S_RCCLK)
+ rt5670->sysclk_src = clk_id;
- dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(component->dev, "Sysclk : %dHz clock id : %d\n", freq, clk_id);
return 0;
}
@@ -2309,8 +2528,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai,
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_codec *codec = dai->codec;
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -2319,18 +2538,18 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5670->pll_in = 0;
rt5670->pll_out = 0;
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ 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_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_MCLK);
break;
case RT5670_PLL1_S_BCLK1:
@@ -2339,38 +2558,38 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
case RT5670_PLL1_S_BCLK4:
switch (dai->id) {
case RT5670_AIF1:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK1);
break;
case RT5670_AIF2:
- snd_soc_update_bits(codec, RT5670_GLB_CLK,
+ snd_soc_component_update_bits(component, RT5670_GLB_CLK,
RT5670_PLL1_SRC_MASK, RT5670_PLL1_SRC_BCLK2);
break;
default:
- dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ dev_err(component->dev, "Invalid dai->id: %d\n", dai->id);
return -EINVAL;
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ 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_write(codec, RT5670_PLL_CTRL1,
+ snd_soc_component_write(component, RT5670_PLL_CTRL1,
pll_code.n_code << RT5670_PLL_N_SFT | pll_code.k_code);
- snd_soc_write(codec, 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);
+ 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;
@@ -2382,7 +2601,7 @@ static int rt5670_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
unsigned int val = 0;
if (rx_mask || tx_mask)
@@ -2420,120 +2639,138 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5670_TDM_CTRL_1, 0x7c00, val);
+ 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_codec *codec,
+static int rt5670_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_PREPARE:
- if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ 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_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_FV1 | RT5670_PWR_FV2,
RT5670_PWR_FV1 | RT5670_PWR_FV2);
- snd_soc_update_bits(codec, RT5670_CHARGE_PUMP,
+ 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_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x1);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
- RT5670_LDO_SEL_MASK, 0x3);
+ 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_update_bits(codec, RT5670_PWR_ANLG1,
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
- RT5670_LDO_SEL_MASK, 0x1);
+ snd_soc_component_update_bits(component, RT5670_PWR_ANLG1,
+ RT5670_LDO_SEL_MASK, 0x3);
break;
case SND_SOC_BIAS_OFF:
- if (rt5670->pdata.jd_mode)
- snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+ 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_update_bits(codec, RT5670_PWR_ANLG1,
+ 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_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+ snd_soc_component_update_bits(component, RT5670_DIG_MISC, 0x1, 0x0);
break;
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-static int rt5670_probe(struct snd_soc_codec *codec)
+static int rt5670_probe(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+ switch (snd_soc_component_read(component, RT5670_RESET) & RT5670_ID_MASK) {
case RT5670_ID_5670:
case RT5670_ID_5671:
- snd_soc_dapm_new_controls(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5670_specific_dapm_widgets,
ARRAY_SIZE(rt5670_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ 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(&codec->dapm,
+ snd_soc_dapm_new_controls(dapm,
rt5672_specific_dapm_widgets,
ARRAY_SIZE(rt5672_specific_dapm_widgets));
- snd_soc_dapm_add_routes(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5672_specific_dapm_routes,
ARRAY_SIZE(rt5672_specific_dapm_routes));
break;
default:
- dev_err(codec->dev,
+ dev_err(component->dev,
"The driver is for RT5670 RT5671 or RT5672 only\n");
return -ENODEV;
}
- rt5670->codec = codec;
+ rt5670->component = component;
return 0;
}
-static int rt5670_remove(struct snd_soc_codec *codec)
+static void rt5670_remove(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regmap_write(rt5670->regmap, RT5670_RESET, 0);
- return 0;
+ snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio);
}
#ifdef CONFIG_PM
-static int rt5670_suspend(struct snd_soc_codec *codec)
+static int rt5670_suspend(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ 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_codec *codec)
+static int rt5670_resume(struct snd_soc_component *component)
{
- struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+ struct rt5670_priv *rt5670 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5670->regmap, false);
regcache_sync(rt5670->regmap);
@@ -2549,12 +2786,12 @@ static int rt5670_resume(struct snd_soc_codec *codec)
#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5670_aif_dai_ops = {
+static const struct snd_soc_dai_ops rt5670_aif_dai_ops = {
.hw_params = rt5670_hw_params,
.set_fmt = rt5670_set_dai_fmt,
- .set_sysclk = rt5670_set_dai_sysclk,
.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[] = {
@@ -2576,6 +2813,7 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
+ .symmetric_rate = 1,
},
{
.name = "rt5670-aif2",
@@ -2595,33 +2833,37 @@ static struct snd_soc_dai_driver rt5670_dai[] = {
.formats = RT5670_FORMATS,
},
.ops = &rt5670_aif_dai_ops,
+ .symmetric_rate = 1,
},
};
-static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
- .probe = rt5670_probe,
- .remove = rt5670_remove,
- .suspend = rt5670_suspend,
- .resume = rt5670_resume,
- .set_bias_level = rt5670_set_bias_level,
- .idle_bias_off = true,
- .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),
+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_rw = true,
+ .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_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = rt5670_reg,
.num_reg_defaults = ARRAY_SIZE(rt5670_reg),
.ranges = rt5670_ranges,
@@ -2629,36 +2871,182 @@ static const struct regmap_config rt5670_regmap = {
};
static const struct i2c_device_id rt5670_i2c_id[] = {
- { "rt5670", 0 },
- { "rt5671", 0 },
- { "rt5672", 0 },
+ { "rt5670" },
+ { "rt5671" },
+ { "rt5672" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
#ifdef CONFIG_ACPI
-static struct acpi_device_id rt5670_acpi_match[] = {
- { "10EC5670", 0},
- { },
+static const struct acpi_device_id rt5670_acpi_match[] = {
+ { "10EC5640" }, /* quirk */
+ { "10EC5670" },
+ { "10EC5672" },
+ { }
};
MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
#endif
-static const struct dmi_system_id dmi_platform_intel_braswell[] = {
+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),
},
{}
};
-static int rt5670_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5670_priv *rt5670;
int ret;
unsigned int val;
@@ -2671,14 +3059,76 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, rt5670);
- if (pdata)
- rt5670->pdata = *pdata;
+ 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 (dmi_check_system(dmi_platform_intel_braswell)) {
- rt5670->pdata.dmic_en = true;
- rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P;
- rt5670->pdata.jd_mode = 1;
+ 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)) {
@@ -2691,7 +3141,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
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);
+ "Device with ID register %#x is not rt5670/72\n", val);
return -ENODEV;
}
@@ -2703,24 +3153,44 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
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);
- if (rt5670->pdata.in2_diff)
+ 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 (i2c->irq) {
+ 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->pdata.jd_mode) {
+ if (rt5670->jd_mode) {
regmap_update_bits(rt5670->regmap, RT5670_GLB_CLK,
RT5670_SCLK_SRC_MASK, RT5670_SCLK_SRC_RCCLK);
rt5670->sysclk = 0;
@@ -2735,7 +3205,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
RT5670_JD_TRI_CBJ_SEL_MASK |
RT5670_JD_TRI_HPO_SEL_MASK,
RT5670_JD_CBJ_JD1_1 | RT5670_JD_HPO_JD1_1);
- switch (rt5670->pdata.jd_mode) {
+ switch (rt5670->jd_mode) {
case 1:
regmap_update_bits(rt5670->regmap, RT5670_A_JD_CTRL1,
RT5670_JD1_MODE_MASK,
@@ -2756,12 +3226,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
}
}
- if (rt5670->pdata.dmic_en) {
+ if (rt5670->dmic_en) {
regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1,
RT5670_GP2_PIN_MASK,
RT5670_GP2_PIN_DMIC1_SCL);
- switch (rt5670->pdata.dmic1_data_pin) {
+ switch (rt5670->dmic1_data_pin) {
case RT5670_DMIC_DATA_IN2P:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_1_DP_MASK,
@@ -2790,7 +3260,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic2_data_pin) {
+ switch (rt5670->dmic2_data_pin) {
case RT5670_DMIC_DATA_IN3N:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL1,
RT5670_DMIC_2_DP_MASK,
@@ -2810,7 +3280,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
break;
}
- switch (rt5670->pdata.dmic3_data_pin) {
+ switch (rt5670->dmic3_data_pin) {
case RT5670_DMIC_DATA_GPIO5:
regmap_update_bits(rt5670->regmap, RT5670_DMIC_CTRL2,
RT5670_DMIC_3_DP_MASK,
@@ -2835,13 +3305,12 @@ static int rt5670_i2c_probe(struct i2c_client *i2c,
pm_runtime_enable(&i2c->dev);
pm_request_idle(&i2c->dev);
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5670,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5670,
rt5670_dai, ARRAY_SIZE(rt5670_dai));
if (ret < 0)
goto err;
- pm_runtime_put(&i2c->dev);
-
return 0;
err:
pm_runtime_disable(&i2c->dev);
@@ -2849,21 +3318,17 @@ err:
return ret;
}
-static int rt5670_i2c_remove(struct i2c_client *i2c)
+static void rt5670_i2c_remove(struct i2c_client *i2c)
{
pm_runtime_disable(&i2c->dev);
- snd_soc_unregister_codec(&i2c->dev);
-
- return 0;
}
static struct i2c_driver rt5670_i2c_driver = {
.driver = {
.name = "rt5670",
- .owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt5670_acpi_match),
},
- .probe = rt5670_i2c_probe,
+ .probe = rt5670_i2c_probe,
.remove = rt5670_i2c_remove,
.id_table = rt5670_i2c_id,
};
diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h
index 21f8e18c13c4..5b230897f630 100644
--- a/sound/soc/codecs/rt5670.h
+++ b/sound/soc/codecs/rt5670.h
@@ -1,19 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5670.h -- RT5670 ALSA SoC audio driver
*
* Copyright 2014 Realtek Microelectronics
* Author: Bard Liao <bardliao@realtek.com>
- *
- * 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 __RT5670_H__
#define __RT5670_H__
-#include <sound/rt5670.h>
-
/* Info */
#define RT5670_RESET 0x00
#define RT5670_VENDOR_ID 0xfd
@@ -217,12 +212,8 @@
/* global definition */
#define RT5670_L_MUTE (0x1 << 15)
#define RT5670_L_MUTE_SFT 15
-#define RT5670_VOL_L_MUTE (0x1 << 14)
-#define RT5670_VOL_L_SFT 14
#define RT5670_R_MUTE (0x1 << 7)
#define RT5670_R_MUTE_SFT 7
-#define RT5670_VOL_R_MUTE (0x1 << 6)
-#define RT5670_VOL_R_SFT 6
#define RT5670_L_VOL_MASK (0x3f << 8)
#define RT5670_L_VOL_SFT 8
#define RT5670_R_VOL_MASK (0x3f)
@@ -760,7 +751,7 @@
#define RT5670_PWR_VREF2_BIT 4
#define RT5670_PWR_FV2 (0x1 << 3)
#define RT5670_PWR_FV2_BIT 3
-#define RT5670_LDO_SEL_MASK (0x3)
+#define RT5670_LDO_SEL_MASK (0x7)
#define RT5670_LDO_SEL_SFT 0
/* Power Management for Analog 2 (0x64) */
@@ -973,12 +964,12 @@
#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 (0x3 << 12)
-#define RT5670_PLL1_SRC_SFT 12
-#define RT5670_PLL1_SRC_MCLK (0x0 << 12)
-#define RT5670_PLL1_SRC_BCLK1 (0x1 << 12)
-#define RT5670_PLL1_SRC_BCLK2 (0x2 << 12)
-#define RT5670_PLL1_SRC_BCLK3 (0x3 << 12)
+#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)
@@ -1816,6 +1807,10 @@
#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) */
@@ -1914,6 +1909,7 @@ enum {
#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)
@@ -1950,17 +1946,20 @@ enum {
};
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,
@@ -1978,13 +1977,27 @@ enum {
RT5670_DOWN_RATE_FILTER = (0x1 << 7),
};
-int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
+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_codec *codec;
- struct rt5670_platform_data pdata;
+ 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;
@@ -1999,6 +2012,18 @@ struct rt5670_priv {
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
index ef6348cb9157..885edcf0a3a5 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677-spi.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * 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.
*/
#include <linux/module.h>
@@ -18,108 +15,619 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
#include <linux/sched.h>
-#include <linux/kthread.h>
#include <linux/uaccess.h>
-#include <linux/miscdevice.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_qos.h>
#include <linux/sysfs.h>
#include <linux/clk.h>
#include <linux/firmware.h>
+#include <linux/acpi.h>
+
+#include <sound/soc.h>
+#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 = snd_soc_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;
+}
-/**
- * rt5677_spi_write - Write data to SPI.
- * @txbuf: Data Buffer for writing.
- * @len: Data length.
+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 = snd_soc_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.
*
- * Returns true for success.
+ * Return: Zero if successful, or a negative error code on failure.
*/
-int rt5677_spi_write(u8 *txbuf, size_t len)
+static int rt5677_spi_copy(struct rt5677_dsp *rt5677_dsp, u32 amount)
{
- int status;
+ int ret = 0;
+ u32 target;
- status = spi_write(g_spi, txbuf, len);
+ if (amount == 0)
+ return ret;
- if (status)
- dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
+ 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));
- return status;
+ 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;
}
-EXPORT_SYMBOL_GPL(rt5677_spi_write);
-/**
- * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
- * @addr: Start address.
- * @txbuf: Data Buffer for writng.
- * @len: Data length, it must be a multiple of 8.
+/*
+ * 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;
+ schedule_delayed_work(&rt5677_dsp->copy_work, secs_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
*
- * Returns true for success.
+ * 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
*/
-int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+static u8 rt5677_spi_select_cmd(bool read, u32 align, u32 remain, u32 *len)
{
- u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
- u8 *write_buf;
- unsigned int i, end, offset = 0;
+ u8 cmd;
- write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
+ 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;
+}
- if (write_buf == NULL)
- return -ENOMEM;
+/* 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);
- while (offset < fw->size) {
- if (offset + RT5677_SPI_BUF_LEN <= fw->size)
- end = RT5677_SPI_BUF_LEN;
- else
- end = fw->size % RT5677_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] = fw->data[offset + i + 0];
- write_buf[i + 11] = fw->data[offset + i + 1];
- write_buf[i + 10] = fw->data[offset + i + 2];
- write_buf[i + 9] = fw->data[offset + i + 3];
- write_buf[i + 8] = fw->data[offset + i + 4];
- write_buf[i + 7] = fw->data[offset + i + 5];
- write_buf[i + 6] = fw->data[offset + i + 6];
- write_buf[i + 5] = fw->data[offset + i + 7];
+ 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;
}
+ }
+}
- write_buf[end + 5] = spi_cmd;
+/* 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;
- rt5677_spi_write(write_buf, end + 6);
+ if (!g_spi)
+ return -ENODEV;
- offset += RT5677_SPI_BUF_LEN;
+ if ((addr & 3) || (len & 3)) {
+ dev_err(&g_spi->dev, "Bad read align 0x%x(%zu)\n", addr, len);
+ return -EACCES;
}
- kfree(write_buf);
+ 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));
- return 0;
+ 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_burst_write);
+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;
- return 0;
+
+ 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[] = {
+ { "10EC5677" },
+ { "RT5677AA" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5677_spi_acpi_id);
+#endif
+
static struct spi_driver rt5677_spi_driver = {
.driver = {
- .name = "rt5677",
- .owner = THIS_MODULE,
+ .name = DRV_NAME,
+ .acpi_match_table = ACPI_PTR(rt5677_spi_acpi_id),
},
.probe = rt5677_spi_probe,
};
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
index ec41b2b3b2ca..088b77931727 100644
--- a/sound/soc/codecs/rt5677-spi.h
+++ b/sound/soc/codecs/rt5677-spi.h
@@ -1,21 +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 <oder_chiou@realtek.com>
- *
- * 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 __RT5677_SPI_H__
#define __RT5677_SPI_H__
-#define RT5677_SPI_BUF_LEN 240
-#define RT5677_SPI_CMD_BURST_WRITE 0x05
-
-int rt5677_spi_write(u8 *txbuf, size_t len);
-int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
+#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
index 5d0bb8748dd1..6b6c690a9e45 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -1,27 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rt5677.c -- RT5677 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * 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.
*/
+#include <linux/delay.h>
+#include <linux/firmware.h>
#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <linux/pm.h>
-#include <linux/of_gpio.h>
+#include <linux/property.h>
#include <linux/regmap.h>
-#include <linux/i2c.h>
-#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/firmware.h>
-#include <linux/gpio.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -36,6 +36,10 @@
#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
@@ -54,7 +58,7 @@ static const struct regmap_range_cfg rt5677_ranges[] = {
},
};
-static const struct reg_default init_list[] = {
+static const struct reg_sequence init_list[] = {
{RT5677_ASRC_12, 0x0018},
{RT5677_PR_BASE + 0x3d, 0x364d},
{RT5677_PR_BASE + 0x17, 0x4fc0},
@@ -62,6 +66,9 @@ static const struct reg_default init_list[] = {
{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)
@@ -293,10 +300,9 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
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_PLL1_CTRL2:
- case RT5677_PLL2_CTRL2:
case RT5677_ASRC_22:
case RT5677_ASRC_23:
case RT5677_VAD_CTRL5:
@@ -305,6 +311,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
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:
@@ -545,14 +553,14 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
* @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_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
mutex_lock(&rt5677->dsp_cmd_lock);
@@ -560,35 +568,35 @@ static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ 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(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ 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(codec->dev, "Failed to set data msb value: %d\n", ret);
+ 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(codec->dev, "Failed to set data lsb value: %d\n", ret);
+ 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(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -600,7 +608,7 @@ err:
/**
* rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @addr: Address index.
* @value: Address data.
*
@@ -610,7 +618,7 @@ err:
static int rt5677_dsp_mode_i2c_read_addr(
struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
{
- struct snd_soc_codec *codec = rt5677->codec;
+ struct snd_soc_component *component = rt5677->component;
int ret;
unsigned int msb, lsb;
@@ -619,21 +627,21 @@ static int rt5677_dsp_mode_i2c_read_addr(
ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
addr >> 16);
if (ret < 0) {
- dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+ 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(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+ 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(codec->dev, "Failed to set op code value: %d\n", ret);
+ dev_err(component->dev, "Failed to set op code value: %d\n", ret);
goto err;
}
@@ -649,7 +657,7 @@ err:
/**
* rt5677_dsp_mode_i2c_write - Write register on DSP mode.
- * rt5677: Private Data.
+ * @rt5677: Private Data.
* @reg: Register index.
* @value: Register data.
*
@@ -665,7 +673,7 @@ static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
/**
* rt5677_dsp_mode_i2c_read - Read register on DSP mode.
- * @codec: SoC audio codec device.
+ * @rt5677: Private Data
* @reg: Register index.
* @value: Register data.
*
@@ -683,106 +691,289 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
-static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
-
if (on) {
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+ 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, 0x2, 0x0);
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP, 0x0);
rt5677->is_dsp_mode = false;
}
}
-static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- static bool activity;
- int ret;
+ 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;
- if (on && !activity) {
+ 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;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, 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.
+ */
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
- regmap_update_bits(rt5677->regmap,
- RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
- RT5677_LDO1_SEL_MASK, 0x0);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
- RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
- RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK,
- RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
- rt5677_set_dsp_mode(codec, true);
-
- ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
- codec->dev);
- if (ret == 0) {
- rt5677_spi_burst_write(0x50000000, rt5677->fw1);
- release_firmware(rt5677->fw1);
- }
+ rt5677_set_vad_source(rt5677);
+ rt5677_set_dsp_mode(rt5677, true);
- ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
- codec->dev);
- if (ret == 0) {
- rt5677_spi_burst_write(0x60000000, rt5677->fw2);
- release_firmware(rt5677->fw2);
+#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;
}
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+ /* 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);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_cache_only(rt5677->regmap, true);
- } else if (!on && activity) {
+ 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;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* 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);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
- rt5677_set_dsp_mode(codec, false);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+ rt5677_set_dsp_mode(rt5677, false);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ /* Disable and clear VAD interrupt */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_mark_dirty(rt5677->regmap);
- regcache_sync(rt5677->regmap);
- }
+ /* 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);
- return 0;
+ mutex_unlock(&rt5677->irq_lock);
+ }
}
-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);
static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
/* {0, +20, +24, +30, +35, +40, +44, +50, +52} dB */
-static unsigned int bst_tlv[] = {
- TLV_DB_RANGE_HEAD(7),
+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),
-};
+ 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)
@@ -790,7 +981,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
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;
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@@ -799,13 +990,8 @@ 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);
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
-
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
- rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+ rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@@ -821,13 +1007,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
/* DAC Digital Volume */
SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
- RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
+ 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, 87, 0, dac_vol_tlv),
+ 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, 87, 0, dac_vol_tlv),
+ 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, 87, 0, dac_vol_tlv),
+ 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),
@@ -899,12 +1085,15 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
static int set_dmic_clk(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
- int idx = rl6231_calc_dmic_clk(rt5677->sysclk);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ 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);
@@ -914,8 +1103,8 @@ static int set_dmic_clk(struct snd_soc_dapm_widget *w,
static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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);
@@ -929,8 +1118,8 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
static int is_using_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -1012,8 +1201,8 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
static int can_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -1021,6 +1210,254 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
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,
@@ -1107,90 +1544,90 @@ static const struct snd_kcontrol_new rt5677_dac_r_mix[] = {
};
static const struct snd_kcontrol_new rt5677_sto1_dac_l_mix[] = {
- SOC_DAPM_SINGLE("ST L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_L_STO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ 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("ST R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_ST_DAC1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC1_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_STO1_DAC_MIXER,
RT5677_M_DAC2_R_STO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_STO1_DAC_MIXER,
+ 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("ST L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_L_MONO_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ 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("ST R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("ST R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_ST_DAC2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC1 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC1_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC2 R Switch", RT5677_MONO_DAC_MIXER,
RT5677_M_DAC2_R_MONO_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC2 L Switch", RT5677_MONO_DAC_MIXER,
+ 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("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_STO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 L Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_L_DD1_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ 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("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_STO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD1_MIXER,
RT5677_M_MONO_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 R Switch", RT5677_DD1_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC3 R Switch", RT5677_DD1_MIXER,
RT5677_M_DAC3_R_DD1_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC3 L Switch", RT5677_DD1_MIXER,
+ 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("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_STO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix L Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_L_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ 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("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Sto DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_STO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("Mono DAC Mix R Switch", RT5677_DD2_MIXER,
RT5677_M_MONO_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 R Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 R Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_R_DD2_R_SFT, 1, 1),
- SOC_DAPM_SINGLE("DAC4 L Switch", RT5677_DD2_MIXER,
+ SOC_DAPM_SINGLE_AUTODISABLE("DAC4 L Switch", RT5677_DD2_MIXER,
RT5677_M_DAC4_L_DD2_R_SFT, 1, 1),
};
@@ -2130,8 +2567,8 @@ static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -2154,8 +2591,8 @@ static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -2178,8 +2615,8 @@ static int rt5677_bst2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -2200,8 +2637,8 @@ static int rt5677_set_pll1_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -2222,8 +2659,8 @@ static int rt5677_set_pll2_event(struct snd_soc_dapm_widget *w,
static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -2249,8 +2686,8 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -2272,8 +2709,8 @@ static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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) {
@@ -2295,12 +2732,12 @@ static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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 (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+ 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,
@@ -2317,6 +2754,21 @@ static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
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 |
@@ -2330,7 +2782,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
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, 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,
@@ -2729,6 +3182,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
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,
@@ -2793,19 +3247,26 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
/* DAC Mixer */
SND_SOC_DAPM_SUPPLY("dac stereo1 filter", RT5677_PWR_DIG2,
- RT5677_PWR_DAC_S1F_BIT, 0, NULL, 0),
+ 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, NULL, 0),
+ 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, NULL, 0),
+ 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, NULL, 0),
+ 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, NULL, 0),
+ 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, NULL, 0),
+ 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, NULL, 0),
+ 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)),
@@ -2878,12 +3339,12 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
- { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
- { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
- { "Stereo3 DMIC Mux", NULL, "DMIC STO3 ASRC", can_use_asrc },
- { "Stereo4 DMIC Mux", NULL, "DMIC STO4 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 },
+ { "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},
@@ -3256,11 +3717,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "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" },
- { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+ /* 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" },
@@ -3284,8 +3758,8 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "IB45 Bypass Mux", "Bypass", "IB45 Mux" },
{ "IB45 Bypass Mux", "Pass SRC", "IB45 Mux" },
- { "IB6 Mux", "IF1 DAC 6", "IF1 DAC6" },
- { "IB6 Mux", "IF2 DAC 6", "IF2 DAC6" },
+ { "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" },
@@ -3293,8 +3767,8 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "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" },
- { "IB7 Mux", "IF2 DAC 7", "IF2 DAC7" },
+ { "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" },
@@ -3635,15 +4109,15 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "DAC1 FS", NULL, "DAC1 MIXL" },
{ "DAC1 FS", NULL, "DAC1 MIXR" },
- { "DAC2 L Mux", "IF1 DAC 2", "IF1 DAC2" },
- { "DAC2 L Mux", "IF2 DAC 2", "IF2 DAC2" },
+ { "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" },
- { "DAC2 R Mux", "IF2 DAC 3", "IF2 DAC3" },
+ { "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" },
@@ -3651,29 +4125,29 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "DAC2 R Mux", "Haptic Generator", "Haptic Generator" },
{ "DAC2 R Mux", "VAD ADC", "VAD ADC Mux" },
- { "DAC3 L Mux", "IF1 DAC 4", "IF1 DAC4" },
- { "DAC3 L Mux", "IF2 DAC 4", "IF2 DAC4" },
+ { "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 DAC4" },
- { "DAC3 R Mux", "IF2 DAC 5", "IF2 DAC4" },
+ { "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" },
- { "DAC4 L Mux", "IF2 DAC 6", "IF2 DAC6" },
+ { "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" },
- { "DAC4 R Mux", "IF2 DAC 7", "IF2 DAC7" },
+ { "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" },
@@ -3813,21 +4287,21 @@ static const struct snd_soc_dapm_route rt5677_dmic2_clk_2[] = {
static int rt5677_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+ 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(codec->dev, "Unsupported frame size: %d\n", frame_size);
+ dev_err(component->dev, "Unsupported frame size: %d\n", frame_size);
return -EINVAL;
}
bclk_ms = frame_size > 32;
@@ -3898,15 +4372,15 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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:
+ case SND_SOC_DAIFMT_CBP_CFP:
rt5677->master[dai->id] = 1;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
reg_val |= RT5677_I2S_MS_S;
rt5677->master[dai->id] = 0;
break;
@@ -3972,8 +4446,8 @@ static int rt5677_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -3990,7 +4464,7 @@ static int rt5677_set_dai_sysclk(struct snd_soc_dai *dai,
reg_val |= RT5677_SCLK_SRC_RCCLK;
break;
default:
- dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ dev_err(component->dev, "Invalid clock id (%d)\n", clk_id);
return -EINVAL;
}
regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
@@ -4025,8 +4499,8 @@ static int rt5677_pll_calc(const unsigned int freq_in,
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_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -4035,7 +4509,7 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
return 0;
if (!freq_in || !freq_out) {
- dev_dbg(codec->dev, "PLL disabled\n");
+ dev_dbg(component->dev, "PLL disabled\n");
rt5677->pll_in = 0;
rt5677->pll_out = 0;
@@ -4075,25 +4549,25 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
}
break;
default:
- dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ 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(codec->dev, "Unsupport input clock %d\n", freq_in);
+ dev_err(component->dev, "Unsupported input clock %d\n", freq_in);
return ret;
}
- dev_dbg(codec->dev, "m_bypass=%d m=%d n=%d k=%d\n",
+ 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);
+ ((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;
@@ -4105,8 +4579,8 @@ static int rt5677_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
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_codec *codec = dai->codec;
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -4133,6 +4607,7 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
break;
case 25:
slot_width_25 = 0x8080;
+ fallthrough;
case 24:
val |= (2 << 8);
break;
@@ -4164,22 +4639,24 @@ static int rt5677_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
-static int rt5677_set_bias_level(struct snd_soc_codec *codec,
+static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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 (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
- rt5677_set_dsp_vad(codec, false);
+ if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
- 0x0055);
+ 5 << RT5677_LDO1_SEL_SFT |
+ 5 << RT5677_LDO2_SEL_SFT);
regmap_update_bits(rt5677->regmap,
RT5677_PR_BASE + RT5677_BIAS_CUR4,
0x0f00, 0x0f00);
@@ -4198,83 +4675,80 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
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_DIG2, 0x0000);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG1, 0x0022);
- regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 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(codec, true);
+ rt5677_set_dsp_vad(component, true);
break;
default:
break;
}
- codec->dapm.bias_level = level;
return 0;
}
-#ifdef CONFIG_GPIOLIB
-static inline struct rt5677_priv *gpio_to_rt5677(struct gpio_chip *chip)
+static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset, int m, int v)
{
- return container_of(chip, struct rt5677_priv, gpio_chip);
+ 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);
}
-static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+#ifdef CONFIG_GPIOLIB
+static int rt5677_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
-
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 1), !!value << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_OUT_MASK, !!value << RT5677_GPIO6_OUT_SFT);
- break;
+ 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;
- default:
- break;
- }
+ return 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 = gpio_to_rt5677(chip);
-
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x3 << (offset * 3 + 1),
- (0x2 | !!value) << (offset * 3 + 1));
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK | RT5677_GPIO6_OUT_MASK,
- RT5677_GPIO6_DIR_OUT | !!value << RT5677_GPIO6_OUT_SFT);
- break;
-
- default:
- break;
- }
+ 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 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int value, ret;
ret = regmap_read(rt5677->regmap, RT5677_GPIO_ST, &value);
@@ -4286,27 +4760,15 @@ static int rt5677_gpio_get(struct gpio_chip *chip, unsigned offset)
static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
-
- switch (offset) {
- case RT5677_GPIO1 ... RT5677_GPIO5:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- 0x1 << (offset * 3 + 2), 0x0);
- break;
-
- case RT5677_GPIO6:
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL3,
- RT5677_GPIO6_DIR_MASK, RT5677_GPIO6_DIR_IN);
- break;
-
- default:
- break;
- }
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
+ int m = RT5677_GPIOx_DIR_MASK;
+ int v = RT5677_GPIOx_DIR_IN;
- return 0;
+ return rt5677_update_gpio_bits(rt5677, offset, m, v);
}
-/** Configures the gpio as
+/*
+ * Configures the GPIO as
* 0 - floating
* 1 - pull down
* 2 - pull up
@@ -4340,46 +4802,37 @@ static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
{
- struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
- struct regmap_irq_chip_data *data = rt5677->irq_data;
+ struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int irq;
- if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) {
- 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 {
- return -ENXIO;
- }
- }
-
- if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) {
- 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;
- }
+ 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 regmap_irq_get_virq(data, irq);
+ return irq_create_mapping(rt5677->domain, irq);
}
-static struct gpio_chip rt5677_template_chip = {
- .label = "rt5677",
+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,
@@ -4396,10 +4849,10 @@ static void rt5677_init_gpio(struct i2c_client *i2c)
rt5677->gpio_chip = rt5677_template_chip;
rt5677->gpio_chip.ngpio = RT5677_GPIO_NUM;
- rt5677->gpio_chip.dev = &i2c->dev;
+ rt5677->gpio_chip.parent = &i2c->dev;
rt5677->gpio_chip.base = -1;
- ret = gpiochip_add(&rt5677->gpio_chip);
+ ret = gpiochip_add_data(&rt5677->gpio_chip, rt5677);
if (ret != 0)
dev_err(&i2c->dev, "Failed to add GPIOs: %d\n", ret);
}
@@ -4425,103 +4878,94 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
}
#endif
-static int rt5677_probe(struct snd_soc_codec *codec)
+static int rt5677_probe(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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->codec = codec;
+ rt5677->component = component;
if (rt5677->pdata.dmic2_clk_pin == RT5677_DMIC_CLK2) {
- snd_soc_dapm_add_routes(&codec->dapm,
+ 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(&codec->dapm,
+ snd_soc_dapm_add_routes(dapm,
rt5677_dmic2_clk_1,
ARRAY_SIZE(rt5677_dmic2_clk_1));
}
- rt5677_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
+ 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]);
- if (rt5677->irq_data) {
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
- 0x8000);
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
- 0x0008);
-
- if (rt5677->pdata.jd1_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD1_MASK,
- rt5677->pdata.jd1_gpio <<
- RT5677_SEL_GPIO_JD1_SFT);
-
- if (rt5677->pdata.jd2_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD2_MASK,
- rt5677->pdata.jd2_gpio <<
- RT5677_SEL_GPIO_JD2_SFT);
-
- if (rt5677->pdata.jd3_gpio)
- regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
- RT5677_SEL_GPIO_JD3_MASK,
- rt5677->pdata.jd3_gpio <<
- RT5677_SEL_GPIO_JD3_SFT);
- }
-
mutex_init(&rt5677->dsp_cmd_lock);
mutex_init(&rt5677->dsp_pri_lock);
return 0;
}
-static int rt5677_remove(struct snd_soc_codec *codec)
+static void rt5677_remove(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
- if (gpio_is_valid(rt5677->pow_ldo2))
- gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ cancel_delayed_work_sync(&rt5677->dsp_work);
- return 0;
+ 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_codec *codec)
+static int rt5677_suspend(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+ 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);
- }
- if (gpio_is_valid(rt5677->pow_ldo2))
- gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
+ 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_codec *codec)
+static int rt5677_resume(struct snd_soc_component *component)
{
- struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
-
- if (gpio_is_valid(rt5677->pow_ldo2)) {
- gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
- msleep(10);
- }
+ 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
@@ -4578,7 +5022,7 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val)
#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
-static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
+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,
@@ -4586,6 +5030,11 @@ static struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.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",
@@ -4682,21 +5131,35 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.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 struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
- .probe = rt5677_probe,
- .remove = rt5677_remove,
- .suspend = rt5677_suspend,
- .resume = rt5677_resume,
- .set_bias_level = rt5677_set_bias_level,
- .idle_bias_off = true,
- .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),
+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 = {
@@ -4732,78 +5195,277 @@ static const struct regmap_config rt5677_regmap = {
.num_ranges = ARRAY_SIZE(rt5677_ranges),
};
-static const struct i2c_device_id rt5677_i2c_id[] = {
- { "rt5677", 0 },
+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[] = {
+ { "10EC5677", RT5677 },
+ { "RT5677CE", RT5677 },
{ }
};
-MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
-static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
+static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
+ struct device *dev)
{
- rt5677->pdata.in1_diff = of_property_read_bool(np,
- "realtek,in1-differential");
- rt5677->pdata.in2_diff = of_property_read_bool(np,
- "realtek,in2-differential");
- rt5677->pdata.lout1_diff = of_property_read_bool(np,
- "realtek,lout1-differential");
- rt5677->pdata.lout2_diff = of_property_read_bool(np,
- "realtek,lout2-differential");
- rt5677->pdata.lout3_diff = of_property_read_bool(np,
- "realtek,lout3-differential");
-
- rt5677->pow_ldo2 = of_get_named_gpio(np,
- "realtek,pow-ldo2-gpio", 0);
+ u32 val;
- /*
- * POW_LDO2 is optional (it may be statically tied on the board).
- * -ENOENT means that the property doesn't exist, i.e. there is no
- * GPIO, so is not an error. Any other error code means the property
- * exists, but could not be parsed.
- */
- if (!gpio_is_valid(rt5677->pow_ldo2) &&
- (rt5677->pow_ldo2 != -ENOENT))
- return rt5677->pow_ldo2;
+ rt5677->pdata.in1_diff =
+ device_property_read_bool(dev, "IN1") ||
+ device_property_read_bool(dev, "realtek,in1-differential");
- of_property_read_u8_array(np, "realtek,gpio-config",
- rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+ rt5677->pdata.in2_diff =
+ device_property_read_bool(dev, "IN2") ||
+ device_property_read_bool(dev, "realtek,in2-differential");
- of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio);
- of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio);
- of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio);
+ rt5677->pdata.lout1_diff =
+ device_property_read_bool(dev, "OUT1") ||
+ device_property_read_bool(dev, "realtek,lout1-differential");
- return 0;
+ 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;
}
-static struct regmap_irq rt5677_irqs[] = {
+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] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD1,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD1,
+ .status_mask = RT5677_STA_GPIO_JD1,
+ .polarity_mask = RT5677_INV_GPIO_JD1,
},
[RT5677_IRQ_JD2] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD2,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD2,
+ .status_mask = RT5677_STA_GPIO_JD2,
+ .polarity_mask = RT5677_INV_GPIO_JD2,
},
[RT5677_IRQ_JD3] = {
- .reg_offset = 0,
- .mask = RT5677_EN_IRQ_GPIO_JD3,
+ .enable_mask = RT5677_EN_IRQ_GPIO_JD3,
+ .status_mask = RT5677_STA_GPIO_JD3,
+ .polarity_mask = RT5677_INV_GPIO_JD3,
},
};
-static struct regmap_irq_chip rt5677_irq_chip = {
- .name = "rt5677",
- .irqs = rt5677_irqs,
- .num_irqs = ARRAY_SIZE(rt5677_irqs),
+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, &reg_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, &reg_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;
+}
+
- .num_regs = 1,
- .status_base = RT5677_IRQ_CTRL1,
- .mask_base = RT5677_IRQ_CTRL1,
- .mask_invert = 1,
+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 &&
@@ -4815,30 +5477,61 @@ static int rt5677_init_irq(struct i2c_client *i2c)
return -EINVAL;
}
- ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
- &rt5677_irq_chip, &rt5677->irq_data);
+ mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
- if (ret != 0) {
- dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
- return ret;
+ /*
+ * 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);
- return 0;
-}
+ /* Set GPIO1 pin to be IRQ output */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
-static void rt5677_free_irq(struct i2c_client *i2c)
-{
- struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+ /* 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;
- if (rt5677->irq_data)
- regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+ return ret;
}
-static int rt5677_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int rt5677_i2c_probe(struct i2c_client *i2c)
{
- struct rt5677_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct device *dev = &i2c->dev;
struct rt5677_priv *rt5677;
int ret;
unsigned int val;
@@ -4848,31 +5541,37 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
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);
- if (pdata)
- rt5677->pdata = *pdata;
+ rt5677->type = (enum rt5677_type)(uintptr_t)device_get_match_data(dev);
+ if (rt5677->type == 0)
+ return -EINVAL;
- if (i2c->dev.of_node) {
- ret = rt5677_parse_dt(rt5677, i2c->dev.of_node);
- if (ret) {
- dev_err(&i2c->dev, "Failed to parse device tree: %d\n",
- ret);
- return ret;
- }
- } else {
- rt5677->pow_ldo2 = -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 (gpio_is_valid(rt5677->pow_ldo2)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5677->pow_ldo2,
- GPIOF_OUT_INIT_HIGH,
- "RT5677 POW_LDO2");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request POW_LDO2 %d: %d\n",
- rt5677->pow_ldo2, 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.
@@ -4900,7 +5599,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
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);
+ "Device with ID register %#x is not rt5677\n", val);
return -ENODEV;
}
@@ -4935,9 +5634,9 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5677->regmap, RT5677_GEN_CTRL2,
RT5677_GPIO5_FUNC_MASK,
RT5677_GPIO5_FUNC_DMIC);
- regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL2,
- RT5677_GPIO5_DIR_MASK,
- RT5677_GPIO5_DIR_OUT);
+ rt5677_update_gpio_bits(rt5677, RT5677_GPIO5,
+ RT5677_GPIOx_DIR_MASK,
+ RT5677_GPIOx_DIR_OUT);
}
if (rt5677->pdata.micbias1_vdd_3v3)
@@ -4946,32 +5645,33 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
RT5677_MICBIAS1_CTRL_VDD_3_3V);
rt5677_init_gpio(i2c);
- rt5677_init_irq(i2c);
+ ret = rt5677_init_irq(i2c);
+ if (ret)
+ dev_err(&i2c->dev, "Failed to initialize irq: %d\n", ret);
- return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_rt5677,
rt5677_dai, ARRAY_SIZE(rt5677_dai));
}
-static int rt5677_i2c_remove(struct i2c_client *i2c)
+static void rt5677_i2c_remove(struct i2c_client *i2c)
{
- snd_soc_unregister_codec(&i2c->dev);
- rt5677_free_irq(i2c);
rt5677_free_gpio(i2c);
-
- return 0;
}
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
- .name = "rt5677",
- .owner = THIS_MODULE,
+ .name = RT5677_DRV_NAME,
+ .of_match_table = rt5677_of_match,
+ .acpi_match_table = rt5677_acpi_match,
},
- .probe = rt5677_i2c_probe,
+ .probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
- .id_table = rt5677_i2c_id,
};
module_i2c_driver(rt5677_i2c_driver);
MODULE_DESCRIPTION("ASoC RT5677 driver");
MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
MODULE_LICENSE("GPL v2");
+
+MODULE_FIRMWARE("rt5677_elf_vad");
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index c0a625f290cc..d67ebae067d9 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1,19 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* rt5677.h -- RT5677 ALSA SoC audio driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
- *
- * 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 __RT5677_H__
#define __RT5677_H__
-#include <sound/rt5677.h>
#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h>
/* Info */
#define RT5677_RESET 0x00
@@ -1339,6 +1336,8 @@
#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)
@@ -1406,9 +1405,87 @@
#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_SRC_MASK (0x7 << 8)
+#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)
@@ -1510,81 +1587,25 @@
#define RT5677_FUNC_MODE_DMIC_GPIO (0x0 << 13)
#define RT5677_FUNC_MODE_JTAG (0x1 << 13)
-/* GPIO Control 2 (0xc1) */
-#define RT5677_GPIO5_DIR_MASK (0x1 << 14)
-#define RT5677_GPIO5_DIR_SFT 14
-#define RT5677_GPIO5_DIR_IN (0x0 << 14)
-#define RT5677_GPIO5_DIR_OUT (0x1 << 14)
-#define RT5677_GPIO5_OUT_MASK (0x1 << 13)
-#define RT5677_GPIO5_OUT_SFT 13
-#define RT5677_GPIO5_OUT_LO (0x0 << 13)
-#define RT5677_GPIO5_OUT_HI (0x1 << 13)
-#define RT5677_GPIO5_P_MASK (0x1 << 12)
-#define RT5677_GPIO5_P_SFT 12
-#define RT5677_GPIO5_P_NOR (0x0 << 12)
-#define RT5677_GPIO5_P_INV (0x1 << 12)
-#define RT5677_GPIO4_DIR_MASK (0x1 << 11)
-#define RT5677_GPIO4_DIR_SFT 11
-#define RT5677_GPIO4_DIR_IN (0x0 << 11)
-#define RT5677_GPIO4_DIR_OUT (0x1 << 11)
-#define RT5677_GPIO4_OUT_MASK (0x1 << 10)
-#define RT5677_GPIO4_OUT_SFT 10
-#define RT5677_GPIO4_OUT_LO (0x0 << 10)
-#define RT5677_GPIO4_OUT_HI (0x1 << 10)
-#define RT5677_GPIO4_P_MASK (0x1 << 9)
-#define RT5677_GPIO4_P_SFT 9
-#define RT5677_GPIO4_P_NOR (0x0 << 9)
-#define RT5677_GPIO4_P_INV (0x1 << 9)
-#define RT5677_GPIO3_DIR_MASK (0x1 << 8)
-#define RT5677_GPIO3_DIR_SFT 8
-#define RT5677_GPIO3_DIR_IN (0x0 << 8)
-#define RT5677_GPIO3_DIR_OUT (0x1 << 8)
-#define RT5677_GPIO3_OUT_MASK (0x1 << 7)
-#define RT5677_GPIO3_OUT_SFT 7
-#define RT5677_GPIO3_OUT_LO (0x0 << 7)
-#define RT5677_GPIO3_OUT_HI (0x1 << 7)
-#define RT5677_GPIO3_P_MASK (0x1 << 6)
-#define RT5677_GPIO3_P_SFT 6
-#define RT5677_GPIO3_P_NOR (0x0 << 6)
-#define RT5677_GPIO3_P_INV (0x1 << 6)
-#define RT5677_GPIO2_DIR_MASK (0x1 << 5)
-#define RT5677_GPIO2_DIR_SFT 5
-#define RT5677_GPIO2_DIR_IN (0x0 << 5)
-#define RT5677_GPIO2_DIR_OUT (0x1 << 5)
-#define RT5677_GPIO2_OUT_MASK (0x1 << 4)
-#define RT5677_GPIO2_OUT_SFT 4
-#define RT5677_GPIO2_OUT_LO (0x0 << 4)
-#define RT5677_GPIO2_OUT_HI (0x1 << 4)
-#define RT5677_GPIO2_P_MASK (0x1 << 3)
-#define RT5677_GPIO2_P_SFT 3
-#define RT5677_GPIO2_P_NOR (0x0 << 3)
-#define RT5677_GPIO2_P_INV (0x1 << 3)
-#define RT5677_GPIO1_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO1_DIR_SFT 2
-#define RT5677_GPIO1_DIR_IN (0x0 << 2)
-#define RT5677_GPIO1_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO1_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO1_OUT_SFT 1
-#define RT5677_GPIO1_OUT_LO (0x0 << 1)
-#define RT5677_GPIO1_OUT_HI (0x1 << 1)
-#define RT5677_GPIO1_P_MASK (0x1 << 0)
-#define RT5677_GPIO1_P_SFT 0
-#define RT5677_GPIO1_P_NOR (0x0 << 0)
-#define RT5677_GPIO1_P_INV (0x1 << 0)
-
-/* GPIO Control 3 (0xc2) */
-#define RT5677_GPIO6_DIR_MASK (0x1 << 2)
-#define RT5677_GPIO6_DIR_SFT 2
-#define RT5677_GPIO6_DIR_IN (0x0 << 2)
-#define RT5677_GPIO6_DIR_OUT (0x1 << 2)
-#define RT5677_GPIO6_OUT_MASK (0x1 << 1)
-#define RT5677_GPIO6_OUT_SFT 1
-#define RT5677_GPIO6_OUT_LO (0x0 << 1)
-#define RT5677_GPIO6_OUT_HI (0x1 << 1)
-#define RT5677_GPIO6_P_MASK (0x1 << 0)
-#define RT5677_GPIO6_P_SFT 0
-#define RT5677_GPIO6_P_NOR (0x0 << 0)
-#define RT5677_GPIO6_P_INV (0x1 << 0)
+/* 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)
@@ -1624,6 +1645,8 @@
#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,
@@ -1647,6 +1670,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
+ RT5677_DSPBUFF,
};
enum {
@@ -1663,10 +1687,86 @@ 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_codec *codec;
+ struct snd_soc_component *component;
+ struct device *dev;
struct rt5677_platform_data pdata;
struct regmap *regmap, *regmap_physical;
const struct firmware *fw1, *fw2;
@@ -1680,14 +1780,29 @@ struct rt5677_priv {
int pll_src;
int pll_in;
int pll_out;
- int pow_ldo2; /* POW_LDO2 pin */
+ 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;
- struct regmap_irq_chip_data *irq_data;
+ 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 000000000000..bba987308e15
--- /dev/null
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 Realtek Semiconductor Corp.
+// Author: Bard Liao <bardliao@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682.h>
+
+#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;
+ }
+
+ regmap_read(rt5682->regmap, RT5682_INT_DEVICE_ID, &val);
+ if (val == 0x6956) {
+ dev_dbg(&i2c->dev, "ALC5682I-VE device\n");
+ rt5682->ve_ic = true;
+ }
+
+ 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 request 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" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match);
+
+static const struct i2c_device_id rt5682_i2c_id[] = {
+ {"rt5682"},
+ {}
+};
+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 <bardliao@realtek.com>");
+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 000000000000..055bea0a4a3b
--- /dev/null
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682-sdw.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2019 Realtek Semiconductor Corp.
+// Author: Oder Chiou <oder_chiou@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/acpi.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Failed to allocate register map: %d\n",
+ __func__, 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, "%s: Device with ID register %x is not rt5682\n", __func__, 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_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, "%s: Invalid clk config", __func__);
+
+ 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 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 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 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) {
+ mutex_lock(&rt5682->disable_irq_lock);
+ if (rt5682->disable_irq == true) {
+ 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt5682_dev_system_suspend, rt5682_dev_resume)
+ RUNTIME_PM_OPS(rt5682_dev_suspend, rt5682_dev_resume, NULL)
+};
+
+static struct sdw_driver rt5682_sdw_driver = {
+ .driver = {
+ .name = "rt5682",
+ .pm = pm_ptr(&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 <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
new file mode 100644
index 000000000000..a0abd2ce0c1e
--- /dev/null
+++ b/sound/soc/codecs/rt5682.c
@@ -0,0 +1,3191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682.c -- RT5682 ALSA SoC audio component driver
+//
+// Copyright 2018 Realtek Semiconductor Corp.
+// Author: Bard Liao <bardliao@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682.h>
+
+#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_INT_DEVICE_ID:
+ 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_INT_DEVICE_ID:
+ 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_CBP_CFP:
+ rt5682->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 int rt5682_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ 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 (req->rate != CLK_48 && req->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);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+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 int rt5682_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682_priv *rt5682 =
+ container_of(hw, struct rt5682_priv,
+ dai_clks_hw[RT5682_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_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(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+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,
+ .determine_rate = rt5682_wclk_determine_rate,
+ .set_rate = rt5682_wclk_set_rate,
+ },
+ [RT5682_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682_bclk_recalc_rate,
+ .determine_rate = rt5682_bclk_determine_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) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ dai_clk_hw);
+ if (ret)
+ return ret;
+ } 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);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x7005);
+ else
+ 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);
+ if (rt5682->ve_ic)
+ regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x2005);
+ else
+ 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 <bardliao@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
new file mode 100644
index 000000000000..de43a5d99403
--- /dev/null
+++ b/sound/soc/codecs/rt5682.h
@@ -0,0 +1,1499 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682.h -- RT5682/RT5658 ALSA SoC audio driver
+ *
+ * Copyright 2018 Realtek Microelectronics
+ * Author: Bard Liao <bardliao@realtek.com>
+ */
+
+#ifndef __RT5682_H__
+#define __RT5682_H__
+
+#include <sound/rt5682.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+#define DEVICE_ID 0x6530
+
+/* Info */
+#define RT5682_RESET 0x0000
+#define RT5682_INT_DEVICE_ID 0x00f9
+#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;
+ bool ve_ic;
+
+#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 000000000000..1d80a4b862e2
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.c
@@ -0,0 +1,3348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt5682s.c -- RT5682I-VS ALSA SoC audio component driver
+//
+// Copyright 2021 Realtek Semiconductor Corp.
+// Author: Derek Fang <derek.fang@realtek.com>
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mutex.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/rt5682s.h>
+
+#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_EN);
+ snd_soc_component_update_bits(component, RT5682S_CBJ_CTRL_1,
+ RT5682S_MB1_PATH_MASK | RT5682S_MB2_PATH_MASK |
+ RT5682S_VREF_POW_MASK, RT5682S_CTRL_MB1_FSM |
+ RT5682S_CTRL_MB2_FSM | RT5682S_VREF_POW_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_NORM | 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_NORM | 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_NORM | 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_DIS);
+ 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_LOW | 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 (!snd_soc_dapm_widget_name_cmp(w, "I2S1") && !rt5682s->wclk_enabled)
+ rt5682s_set_i2s(rt5682s, RT5682S_AIF1, on);
+ else if (!snd_soc_dapm_widget_name_cmp(w, "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_CBP_CFP:
+ rt5682s->master[dai->id] = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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 int rt5682s_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ 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 (req->rate != CLK_48 && req->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);
+ req->rate = CLK_48;
+ }
+
+ return 0;
+}
+
+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 int rt5682s_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ struct rt5682s_priv *rt5682s =
+ container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
+ unsigned long factor;
+
+ if (!req->best_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(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
+
+ return 0;
+}
+
+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,
+ .determine_rate = rt5682s_wclk_determine_rate,
+ .set_rate = rt5682s_wclk_set_rate,
+ },
+ [RT5682S_DAI_BCLK_IDX] = {
+ .recalc_rate = rt5682s_bclk_recalc_rate,
+ .determine_rate = rt5682s_bclk_determine_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) {
+ ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw);
+ if (ret)
+ return ret;
+ } 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);
+ device_property_read_u32(dev, "realtek,ldo-sel",
+ &rt5682s->pdata.ldo_dacref);
+
+ 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;
+ }
+
+ /* LDO output voltage control */
+ switch (rt5682s->pdata.ldo_dacref) {
+ case RT5682S_LDO_1_607V:
+ break;
+ case RT5682S_LDO_1_5V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_5V);
+ break;
+ case RT5682S_LDO_1_406V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_406V);
+ break;
+ case RT5682S_LDO_1_731V:
+ regmap_update_bits(rt5682s->regmap, RT5682S_BIAS_CUR_CTRL_7,
+ RT5682S_LDO_DACREF_MASK, RT5682S_LDO_DACREF_1_731V);
+ break;
+ default:
+ dev_warn(&i2c->dev, "invalid LDO output setting.\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 request 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"},
+ {}
+};
+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 <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
new file mode 100644
index 000000000000..67f42898de96
--- /dev/null
+++ b/sound/soc/codecs/rt5682s.h
@@ -0,0 +1,1492 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt5682s.h -- RT5682I-VS ALSA SoC audio driver
+ *
+ * Copyright 2021 Realtek Microelectronics
+ * Author: Derek Fang <derek.fang@realtek.com>
+ */
+
+#ifndef __RT5682S_H__
+#define __RT5682S_H__
+
+#include <sound/rt5682s.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+
+/* 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)
+
+/* Bias current control 7 (0x0110) */
+#define RT5682S_LDO_DACREF_MASK (0x3 << 4)
+#define RT5682S_LDO_DACREF_1_607V (0x0 << 4)
+#define RT5682S_LDO_DACREF_1_5V (0x1 << 4)
+#define RT5682S_LDO_DACREF_1_406V (0x2 << 4)
+#define RT5682S_LDO_DACREF_1_731V (0x3 << 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 000000000000..44543c0da177
--- /dev/null
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -0,0 +1,572 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700-sdw.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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 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 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 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt700_dev_system_suspend, rt700_dev_resume)
+ RUNTIME_PM_OPS(rt700_dev_suspend, rt700_dev_resume, NULL)
+};
+
+static struct sdw_driver rt700_sdw_driver = {
+ .driver = {
+ .name = "rt700",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+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 000000000000..4ad0dcfd16fd
--- /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 000000000000..816117c13aea
--- /dev/null
+++ b/sound/soc/codecs/rt700.c
@@ -0,0 +1,1241 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt700.c -- rt700 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#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("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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, &reg);
+ 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_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;
+ }
+
+ 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, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt700->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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_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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h
new file mode 100644
index 000000000000..491774d207de
--- /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 000000000000..6eb05871db37
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -0,0 +1,496 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdw-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+
+#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++;
+ }
+
+ prop->dp0_prop = devm_kzalloc(&slave->dev, sizeof(*prop->dp0_prop),
+ GFP_KERNEL);
+ if (!prop->dp0_prop)
+ return -ENOMEM;
+
+ prop->dp0_prop->simple_ch_prep_sm = true;
+ prop->dp0_prop->ch_prep_timeout = 10;
+
+ /* 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 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 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 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) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_system_suspend, rt711_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt711-sdca",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+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 000000000000..0d774e473ab9
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+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 000000000000..16c351779243
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -0,0 +1,1672 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2021 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#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,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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 void rt711_sdca_ge_force_jack_type(struct rt711_sdca_priv *rt711, unsigned int det_mode)
+{
+ switch (det_mode) {
+ case 0x00:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x0000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x10, 0x00);
+ break;
+ case 0x03:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8000);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x13);
+ break;
+ case 0x05:
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG, RT711_COMBO_JACK_AUTO_CTL1, 0x8400, 0x8400);
+ rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL, RT711_PUSH_BTN_INT_CTL0, 0x17, 0x15);
+ break;
+ }
+}
+
+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;
+
+ rt711_sdca_ge_force_jack_type(rt711, rt711->ge_mode_override);
+
+ /* 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_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 int rt711_sdca_ge_select_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, item;
+
+ val = (rt711->ge_mode_override >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[0] = item;
+ return 0;
+}
+
+static int rt711_sdca_ge_select_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+ unsigned int val, change = 0;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ if (rt711->ge_mode_override != val) {
+ rt711->ge_mode_override = val;
+ change = 1;
+ }
+
+ return change;
+}
+
+static const char * const rt711_sdca_ge_select[] = {
+ "Auto",
+ "Headphone",
+ "Headset",
+};
+
+static int rt711_sdca_ge_select_values[] = {
+ 0,
+ 3,
+ 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt711_sdca_ge_mode_enum, SND_SOC_NOPM,
+ 0, 0x7, rt711_sdca_ge_select, rt711_sdca_ge_select_values);
+
+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),
+ SOC_ENUM_EXT("GE49 Selected Mode", rt711_sdca_ge_mode_enum,
+ rt711_sdca_ge_select_get, rt711_sdca_ge_select_put),
+};
+
+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, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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, "%s: Rate %d is not supported\n",
+ __func__, 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_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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
new file mode 100644
index 000000000000..15263dcb0314
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -0,0 +1,244 @@
+/* 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 <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+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;
+ unsigned int ge_mode_override;
+};
+
+/* 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 000000000000..93a5a89a96b1
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -0,0 +1,585 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711-sdw.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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 0x752008:
+ case 0x752009:
+ case 0x75200b:
+ 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, "%s: Invalid clk config", __func__);
+
+ 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 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 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 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) {
+ mutex_lock(&rt711->disable_irq_lock);
+ if (rt711->disable_irq == true) {
+ 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume)
+ RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdw_driver = {
+ .driver = {
+ .name = "rt711",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdw.h b/sound/soc/codecs/rt711-sdw.h
new file mode 100644
index 000000000000..6acf9858330d
--- /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 000000000000..af3a49aee618
--- /dev/null
+++ b/sound/soc/codecs/rt711.c
@@ -0,0 +1,1341 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// rt711.c -- rt711 ALSA SoC audio driver
+//
+// Copyright(c) 2019 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+#include <sound/jack.h>
+
+#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("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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, &reg);
+ 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, "%s: Wrong JD source\n", __func__);
+ 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_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, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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_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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h
new file mode 100644
index 000000000000..491e357191f9
--- /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 000000000000..42f8f7b8bed0
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -0,0 +1,987 @@
+// 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 <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#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,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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_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, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - 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, &regvalue[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, "%s: 0x%08x can't be set\n",
+ __func__, 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, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 4) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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, "%s: Rate %d is not supported\n",
+ __func__, 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 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 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 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, "%s: Initialization not complete, timed out\n",
+ __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL)
+};
+
+
+static const 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",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+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 000000000000..110154e74efe
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+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 000000000000..ea07131edfa2
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-sdw.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#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 SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 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 SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 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 ... 0x61000f1:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, 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_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_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000020:
+ case 0x2000024:
+ case 0x2000030:
+ 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;
+
+ sdw_slave_read_prop(slave);
+
+ 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(8) | BIT(4); /* BITMAP: 100010000 */
+ 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 const 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 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 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 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) {
+ mutex_lock(&rt712->disable_irq_lock);
+ if (rt712->disable_irq == true) {
+
+ 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt712_sdca_dev_system_suspend, rt712_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt712_sdca_dev_suspend, rt712_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt712_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca",
+ .pm = pm_ptr(&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 <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h
new file mode 100644
index 000000000000..99fd2d67f04d
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -0,0 +1,75 @@
+/* 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 <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt712_sdca_reg_defaults[] = {
+
+ { 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_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_MUTE, CH_02), 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_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { 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_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { 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_CS31, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { 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 },
+ { 0x200004a, 0x8830 },
+ { 0x2000067, 0xf100 },
+ { 0x5800000, 0x1893 },
+ { 0x5b00000, 0x0407 },
+ { 0x5b00005, 0x0000 },
+ { 0x5b00029, 0x3fff },
+ { 0x5b0002a, 0xf000 },
+ { 0x6100000, 0x04e4 },
+ { 0x610000e, 0x0007 },
+ { 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_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, 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_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 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02), 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 000000000000..0ebaae426e73
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -0,0 +1,1932 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca.c -- rt712 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdca.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#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,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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 */
+ if (rt712->version_id == RT712_VA)
+ 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 */
+ if (rt712->version_id == RT712_VA)
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+ else
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT712_SDCA_ENT_HID01,
+ RT712_SDCA_CTL_HIDTX_CURRENT_OWNER, 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, VB uses JD1 in default */
+ if (rt712->version_id == RT712_VA)
+ 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_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_01, ch_02;
+
+ ch_01 = (rt712->fu0f_dapm_mute || rt712->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (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_01), ch_01);
+ 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_02), ch_02);
+ 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 int rt712_sdca_set_fu05_playback_ctl(struct rt712_sdca_priv *rt712)
+{
+ int err;
+ unsigned int ch_01, ch_02;
+
+ ch_01 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_l_mute) ? 0x01 : 0x00;
+ ch_02 = (rt712->fu05_dapm_mute || rt712->fu05_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_01), ch_01);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05,
+ RT712_SDCA_CTL_FU_MUTE, CH_02), ch_02);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_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->fu05_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt712->fu05_mixer_r_mute;
+ return 0;
+}
+
+static int rt712_sdca_fu05_playback_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->fu05_mixer_l_mute == !ucontrol->value.integer.value[0] &&
+ rt712->fu05_mixer_r_mute == !ucontrol->value.integer.value[1])
+ return 0;
+
+ rt712->fu05_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt712->fu05_mixer_r_mute = !ucontrol->value.integer.value[1];
+
+ err = rt712_sdca_set_fu05_playback_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_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU05, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 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_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_USER_FU0F, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 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_01),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_PLATFORM_FU44, RT712_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 8, 3, 0,
+ rt712_sdca_set_gain_get, rt712_sdca_set_gain_put, boost_vol_tlv),
+ SOC_DOUBLE_EXT("FU05 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt712_sdca_fu05_playback_get, rt712_sdca_fu05_playback_put),
+};
+
+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_01),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_VOLUME, CH_02),
+ 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);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu05_dapm_mute = false;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu05_dapm_mute = true;
+ rt712_sdca_set_fu05_playback_ctl(rt712);
+ 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_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 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_l_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 0, 1, 1);
+static const struct snd_kcontrol_new rt712_spk_r_dac =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT_USER_FU06, RT712_SDCA_CTL_FU_MUTE, CH_02),
+ 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_PGA("FU06", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_pde23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ /* Output */
+ SND_SOC_DAPM_SWITCH("OT23 L", SND_SOC_NOPM, 0, 0, &rt712_spk_l_dac),
+ SND_SOC_DAPM_SWITCH("OT23 R", SND_SOC_NOPM, 0, 0, &rt712_spk_r_dac),
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_spk_dapm_routes[] = {
+ { "FU06", NULL, "DP3RX" },
+ { "FU06", NULL, "PDE 23" },
+ { "OT23 L", "Switch", "FU06" },
+ { "OT23 R", "Switch", "FU06" },
+ { "SPOL", NULL, "OT23 L" },
+ { "SPOR", NULL, "OT23 R" },
+};
+
+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 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_dmic_kctrl_priv *p =
+ (struct rt712_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, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - 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_dmic_kctrl_priv *p =
+ (struct rt712_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, &regvalue[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_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_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_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_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_dmic_kctrl_priv *p =
+ (struct rt712_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_dmic_kctrl_priv *p =
+ (struct rt712_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_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(dmic_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, dmic_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_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (((val >> mask_sft) & 0xf) == 0x4) ? 0 : 1;
+
+ 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_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 0A Mux"))
+ mask_sft = 0;
+ else if (strstr(ucontrol->id.name, "ADC 0B Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = ((0xf << mask_sft) & val2) >> mask_sft;
+
+ if (val == 0)
+ val = 0x4;
+ else if (val >= 1)
+ val = 0xe;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, 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_dmic_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0a_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc0b_enum, SND_SOC_NOPM, 0, adc_dmic_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0a_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0A Mux", rt712_adc0a_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc0b_mux =
+ SOC_DAPM_ENUM_EXT("ADC 0B Mux", rt712_adc0b_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_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_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 0A Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0a_mux),
+ SND_SOC_DAPM_MUX("ADC 0B Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc0b_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP8TX", "DP8 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP8TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 0A Mux"},
+ {"FU 1E", NULL, "ADC 0B Mux"},
+ {"ADC 0A Mux", "DMIC1", "DMIC1"},
+ {"ADC 0A Mux", "DMIC2", "DMIC2"},
+ {"ADC 0B Mux", "DMIC1", "DMIC1"},
+ {"ADC 0B Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->dmic_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 = {
+ .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 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,
+#ifdef CONFIG_DEBUG_FS
+ .debugfs_prefix = "dmic",
+#endif
+};
+
+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 id %d", __func__, dai->name, dai->id);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ /* VA doesn't support AIF3 */
+ if (dai->id == RT712_AIF3 && rt712->version_id == RT712_VA)
+ 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 if (dai->id == RT712_AIF3)
+ port = 8;
+ 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, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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, "%s: Rate %d is not supported\n",
+ __func__, 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;
+ case RT712_AIF3:
+ 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);
+ break;
+ default:
+ dev_err(component->dev, "%s: Wrong DAI id\n", __func__);
+ 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,
+ }
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-aif3",
+ .id = RT712_AIF3,
+ .capture = {
+ .stream_name = "DP8 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .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;
+ 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;
+ rt712->fu05_dapm_mute = true;
+ rt712->fu05_mixer_l_mute = rt712->fu05_mixer_r_mute = false;
+
+ /* 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;
+
+ /* only add the dmic component if a SMART_MIC function is exposed in ACPI */
+ if (sdca_device_quirk_match(slave, SDCA_QUIRKS_RT712_VB)) {
+ 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;
+ rt712->dmic_function_found = true;
+ }
+
+ /* 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 rt712_sdca_va_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int hibernation_flag;
+ struct device *dev = &rt712->slave->dev;
+
+ 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);
+ }
+}
+
+static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
+{
+ int ret = 0;
+ unsigned int jack_func_status, mic_func_status, amp_func_status;
+ struct device *dev = &rt712->slave->dev;
+
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ regmap_read(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s jack/mic/amp func_status=0x%x, 0x%x, 0x%x\n",
+ __func__, jack_func_status, mic_func_status, amp_func_status);
+
+ /* DMIC */
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_IT_FLOAT_CTL, 0x1526);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_DMIC2_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL, 0x1f1e);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_ADC0B_FU_CH12_FLOAT_CTL, 0x0304);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_HDA_LEGACY_CONFIG_CTL0, 0x8010);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT11, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_ULTRA_SOUND_DET, RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* Jack */
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_IMS_DRE, RT712_SEL_VEE2_HP_CTL1, 0x042a);
+ rt712_sdca_index_write(rt712, RT712_CHARGE_PUMP, RT712_HP_DET_CTL3, 0x1fff);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec67);
+ 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);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_JD_CTL1, 0x2802);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL6, 0xf215);
+
+ /* calibration */
+ 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);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT_IT09, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_HDA_CTL, RT712_MISC_CTL_FOR_UAJ, 0x0003);
+
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+
+ /* SPK */
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt712->first_hw_init)) {
+ if (rt712->hw_id != RT712_DEV_ID_713) {
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_IO_CTL, 0xec63);
+ rt712_sdca_index_write(rt712, RT712_VENDOR_REG, RT712_CLASSD_AMP_CTL1, 0xfff5);
+ 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);
+ }
+ /* clear flag */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT712_SDCA_ENT0, RT712_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
+ unsigned int val;
+ struct sdw_slave_prop *prop = &slave->prop;
+
+ 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->version_id = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_id=0x%x, version_id=0x%x\n", __func__, rt712->hw_id, rt712->version_id);
+
+ if (rt712->version_id == RT712_VA) {
+ if (rt712->dmic_function_found) {
+ dev_err(&slave->dev, "%s RT712 VA detected but SMART_MIC function exposed in ACPI\n",
+ __func__);
+ goto suspend;
+ }
+
+ rt712_sdca_va_io_init(rt712);
+ } else {
+ if (!rt712->dmic_function_found)
+ dev_warn(&slave->dev, "%s RT712 VB detected but no SMART_MIC function exposed in ACPI\n",
+ __func__);
+
+ /* multilanes and DMIC are supported by rt712vb */
+ prop->lane_control_support = true;
+ rt712_sdca_vb_io_init(rt712);
+ }
+
+ /*
+ * 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);
+
+ 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;
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+
+suspend:
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
new file mode 100644
index 000000000000..7ab7d5feb50a
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -0,0 +1,261 @@
+/* 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 <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt712_sdca_priv {
+ struct regmap *regmap;
+ struct regmap *mbq_regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_component *dmic_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;
+ unsigned int version_id;
+ bool dmic_function_found;
+ bool fu0f_dapm_mute;
+ bool fu0f_mixer_l_mute;
+ bool fu0f_mixer_r_mute;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+ bool fu05_dapm_mute;
+ bool fu05_mixer_l_mute;
+ bool fu05_mixer_r_mute;
+};
+
+struct rt712_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
+
+/* NID */
+#define RT712_VENDOR_REG 0x20
+#define RT712_EQ_CTRL 0x53
+#define RT712_CHARGE_PUMP 0x57
+#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_JD_CTL1 0x09
+#define RT712_IO_CTL 0x0c
+#define RT712_LDO2_3_CTL1 0x0e
+#define RT712_PARA_VERB_CTL 0x1a
+#define RT712_CC_DET1 0x24
+#define RT712_CLASSD_AMP_CTL1 0x37
+#define RT712_CLASSD_AMP_CTL6 0x3c
+#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:57h) */
+#define RT712_HP_DET_CTL3 0x0c
+
+/* 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_SEL_VEE2_HP_CTL1 0x23
+#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
+#define RT712_MISC_CTL_FOR_UAJ 0x72
+#define RT712_ADC0A_CS_ADC0B_FU_FLOAT_CTL 0xa2
+#define RT712_DMIC2_FU_IT_FLOAT_CTL 0xa6
+#define RT712_ADC0B_FU_CH12_FLOAT_CTL 0xb0
+#define RT712_DMIC2_FU_CH12_FLOAT_CTL 0xb1
+
+/* 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_ENT0 0x00
+#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_IT11 0x26
+#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
+#define RT712_SDCA_CTL_FUNC_STATUS 0x10
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+#define FUNCTION_HAS_BEEN_RESET BIT(6)
+#define FUNCTION_BUSY BIT(7)
+
+/* 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,
+ RT712_AIF3,
+};
+
+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
+
+enum rt712_sdca_version {
+ RT712_VA,
+ RT712_VB,
+};
+
+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 000000000000..ce7d8955efc3
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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 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 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->initialization_complete,
+ msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715-sdca",
+ .pm = pm_ptr(&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 <jack.yu@realtek.com>");
+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 000000000000..0cbc14844f8c
--- /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 <linux/soundwire/sdw_registers.h>
+
+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 000000000000..db7d43349d7d
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -0,0 +1,1075 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#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,
+ "%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+
+ 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,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, 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, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, 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, "%s: Failed to write 0x%x=0x%x\n",
+ __func__, 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, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, 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, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, 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, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, 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, "%s: Failed to read 0x%x, ret=%d\n",
+ __func__, 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, -1725, 75, 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 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, 0x3f, 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, 0x3f),
+ 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, 0x3f),
+ /* 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, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(component->dev, "%s: Unable to configure port, retval:%d\n",
+ __func__, 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, "%s: Unsupported sample rate %d\n",
+ __func__, 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-sdca-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-sdca-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_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+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 000000000000..e5d6928ecaba
--- /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 <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+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 000000000000..a3df4bbedf86
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -0,0 +1,551 @@
+// 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 <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#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 0x752001:
+ 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,
+};
+
+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, "%s: Invalid clk config", __func__);
+
+ 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 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 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, "%s: Initialization not complete, timed out\n", __func__);
+ 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 = {
+ SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+ RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+ .driver = {
+ .name = "rt715",
+ .pm = pm_ptr(&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 <jack.yu@realtek.com>");
+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 000000000000..5d7661e335ae
--- /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 000000000000..7e1628a5c9d1
--- /dev/null
+++ b/sound/soc/codecs/rt715.c
@@ -0,0 +1,1140 @@
+// 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 <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/hda_verbs.h>
+
+#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("%s: Failed to set private value: %08x <= %04x %d\n",
+ __func__, addr, value, ret);
+ }
+
+ 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("%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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("%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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 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;
+ }
+
+ 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, "%s: Invalid DAI id %d\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ retval = sdw_stream_add_slave(rt715->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "%s: Unable to configure port\n", __func__);
+ 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, "%s: Unsupported sample rate %d\n",
+ __func__, 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, "%s: Unsupported channels %d\n",
+ __func__, 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_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver");
+MODULE_DESCRIPTION("ASoC rt715 driver SDW");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
new file mode 100644
index 000000000000..a0c56aa1003a
--- /dev/null
+++ b/sound/soc/codecs/rt715.h
@@ -0,0 +1,224 @@
+/* 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 <linux/regulator/consumer.h>
+
+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 rt715_clock_config(struct device *dev);
+#endif /* __RT715_H__ */
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
new file mode 100644
index 000000000000..4d8a12b13015
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca-sdw.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt721-sdca.h"
+#include "rt721-sdca-sdw.h"
+#include "rt-sdw-common.h"
+
+static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35:
+ case 0x2f50:
+ case 0x2f51:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01:
+ case 0x2f51:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+ RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+ RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0900004 ... 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x0310100:
+ case 0x2000000 ... 0x2000003:
+ case 0x2000013:
+ case 0x200002c:
+ case 0x200003c:
+ case 0x2000046:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ case 0x5f00045:
+ case 0x5f00048:
+ case 0x6100000:
+ case 0x6100005:
+ case 0x6100006:
+ case 0x610000d:
+ case 0x6100010:
+ case 0x6100011:
+ case 0x6100013:
+ case 0x6100015:
+ case 0x6100017:
+ case 0x6100025:
+ case 0x6100029:
+ case 0x610002c ... 0x610002f:
+ case 0x6100053 ... 0x6100055:
+ case 0x6100057:
+ case 0x610005a:
+ case 0x610005b:
+ case 0x610006a:
+ case 0x610006d:
+ case 0x6100092:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0310100:
+ case 0x0900005:
+ case 0x0900009:
+ case 0x0a00005:
+ case 0x0c00005:
+ case 0x0d00014:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x200002c:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x5810000:
+ case 0x5810036:
+ case 0x5810037:
+ case 0x5810038:
+ case 0x5810039:
+ case 0x5b10018:
+ case 0x5b10019:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt721_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt721_sdca_readable_register,
+ .volatile_reg = rt721_sdca_volatile_register,
+ .max_register = 0x44ffffff,
+ .reg_defaults = rt721_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt721_sdca_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt721_sdca_mbq_readable_register,
+ .volatile_reg = rt721_sdca_mbq_volatile_register,
+ .max_register = 0x41000312,
+ .reg_defaults = rt721_sdca_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt721_sdca_mbq_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt721_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt721->hw_init = false;
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (rt721->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 rt721_sdca_jack_init set.
+ */
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->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 (rt721->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt721_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt721_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;
+
+ sdw_slave_read_prop(slave);
+ 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 = 1380;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt721-sdca codec */
+ prop->lane_control_support = true;
+
+ return 0;
+}
+
+static int rt721_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct rt721_sdca_priv *rt721 = 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(&rt721->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 (rt721->scp_sdca_stat2)
+ scp_sdca_stat2 = rt721->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(&rt721->disable_irq_lock);
+
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat1 = ret;
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+
+ rt721->scp_sdca_stat2 = ret;
+ if (scp_sdca_stat2)
+ rt721->scp_sdca_stat2 |= scp_sdca_stat2;
+ do {
+ /* clear flag */
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+ if (ret < 0)
+ goto io_error;
+ }
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ goto io_error;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(rt721->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(rt721->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(rt721->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(rt721->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__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+ ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+
+ if (status->sdca_cascade && !rt721->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &rt721->jack_detect_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&rt721->disable_irq_lock);
+
+ return 0;
+
+io_error:
+ mutex_unlock(&rt721->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops rt721_sdca_slave_ops = {
+ .read_prop = rt721_sdca_read_prop,
+ .interrupt_callback = rt721_sdca_interrupt_callback,
+ .update_status = rt721_sdca_update_status,
+};
+
+static int rt721_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, &rt721_sdca_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt721_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt721_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+ if (rt721->hw_init) {
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+ }
+
+ if (rt721->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&rt721->calibrate_mutex);
+ mutex_destroy(&rt721->disable_irq_lock);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt721_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt721_sdca_id);
+
+static int rt721_sdca_dev_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ if (!rt721->hw_init)
+ return 0;
+
+ cancel_delayed_work_sync(&rt721->jack_detect_work);
+ cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ return 0;
+}
+
+static int rt721_sdca_dev_system_suspend(struct device *dev)
+{
+ struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret1, ret2;
+
+ if (!rt721_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(&rt721_sdca->disable_irq_lock);
+ rt721_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(&rt721_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 rt721_sdca_dev_suspend(dev);
+}
+
+#define RT721_PROBE_TIMEOUT 5000
+
+static int rt721_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt721->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request) {
+ mutex_lock(&rt721->disable_irq_lock);
+ if (rt721->disable_irq == true) {
+ 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);
+ rt721->disable_irq = false;
+ }
+ mutex_unlock(&rt721->disable_irq_lock);
+ goto regmap_sync;
+ }
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT721_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(rt721->regmap, false);
+ regcache_sync(rt721->regmap);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ regcache_sync(rt721->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt721_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt721_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt721-sdca",
+ .owner = THIS_MODULE,
+ .pm = pm_ptr(&rt721_sdca_pm),
+ },
+ .probe = rt721_sdca_sdw_probe,
+ .remove = rt721_sdca_sdw_remove,
+ .ops = &rt721_sdca_slave_ops,
+ .id_table = rt721_sdca_id,
+};
+module_sdw_driver(rt721_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h
new file mode 100644
index 000000000000..214b31b82583
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca-sdw.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_SDW_H__
+#define __RT721_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt721_sdca_reg_defaults[] = {
+ { 0x202d, 0x00 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x08 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0e },
+ { 0x2f06, 0x01 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x00 },
+ { 0x2f35, 0x00 },
+ { 0x2f50, 0xf0 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x2f5a, 0x00 },
+ { 0x2f5b, 0x07 },
+ { 0x2f5c, 0x27 },
+ { 0x2f5d, 0x07 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+};
+
+static const struct reg_default rt721_sdca_mbq_defaults[] = {
+ { 0x0900007, 0xc004 },
+ { 0x2000001, 0x0000 },
+ { 0x2000002, 0x0000 },
+ { 0x2000003, 0x0000 },
+ { 0x2000013, 0x8001 },
+ { 0x200003c, 0x0000 },
+ { 0x2000046, 0x3400 },
+ { 0x5f00044, 0x6040 },
+ { 0x5f00045, 0x3333 },
+ { 0x5f00048, 0x0000 },
+ { 0x6100005, 0x0005 },
+ { 0x6100006, 0x0000 },
+ { 0x610000d, 0x0051 },
+ { 0x6100010, 0x0180 },
+ { 0x6100011, 0x0000 },
+ { 0x6100013, 0x0000 },
+ { 0x6100015, 0x0000 },
+ { 0x6100017, 0x8049 },
+ { 0x6100025, 0x1000 },
+ { 0x6100029, 0x0809 },
+ { 0x610002c, 0x2828 },
+ { 0x610002d, 0x2929 },
+ { 0x610002e, 0x3529 },
+ { 0x610002f, 0x2901 },
+ { 0x6100053, 0x2630 },
+ { 0x6100054, 0x2a2a },
+ { 0x6100055, 0x152f },
+ { 0x6100057, 0x2200 },
+ { 0x610005a, 0x2a4b },
+ { 0x610005b, 0x2a00 },
+ { 0x610006a, 0x0102 },
+ { 0x610006d, 0x0102 },
+ { 0x6100092, 0x4f61 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_L), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+ CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_L), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+ CH_R), 0xfe00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_02),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_03),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_04),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R),
+ 0x0000 },
+};
+
+#endif /* __RT721_SDW_H__ */
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
new file mode 100644
index 000000000000..a4bd29d7220b
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -0,0 +1,1560 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt721-sdca.h"
+#include "rt-sdw-common.h"
+
+static void rt721_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_sdca_priv, jack_detect_work.work);
+ int btn_type = 0;
+
+ if (!rt721->hs_jack)
+ return;
+
+ if (!rt721->component->card || !rt721->component->card->instantiated)
+ return;
+
+ /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
+ if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ rt721->jack_type = rt_sdca_headset_detect(rt721->regmap,
+ RT721_SDCA_ENT_GE49);
+ if (rt721->jack_type < 0)
+ return;
+ }
+
+ /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+ if (rt721->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+ btn_type = rt_sdca_button_detect(rt721->regmap,
+ RT721_SDCA_ENT_HID01, RT721_BUF_ADDR_HID1,
+ RT721_SDCA_HID_ID);
+
+ if (rt721->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(&rt721->slave->dev,
+ "in %s, jack_type=%d\n", __func__, rt721->jack_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, btn_type=0x%x\n", __func__, btn_type);
+ dev_dbg(&rt721->slave->dev,
+ "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+ rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+
+ snd_soc_jack_report(rt721->hs_jack, rt721->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(rt721->hs_jack, rt721->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,
+ &rt721->jack_btn_check_work, msecs_to_jiffies(200));
+ }
+}
+
+static void rt721_sdca_btn_check_handler(struct work_struct *work)
+{
+ struct rt721_sdca_priv *rt721 =
+ container_of(work, struct rt721_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(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+ RT721_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(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+ RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret < 0)
+ goto io_error;
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(rt721->regmap,
+ RT721_BUF_ADDR_HID1 + offset + idx, &val);
+ if (ret < 0)
+ goto io_error;
+ buf[idx] = val & 0xff;
+ }
+ /* Report ID for HID1 */
+ if (buf[0] == 0x11)
+ btn_type = rt_sdca_btn_type(&buf[1]);
+ } else
+ rt721->jack_type = 0;
+
+ dev_dbg(&rt721->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type);
+ snd_soc_jack_report(rt721->hs_jack, rt721->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(rt721->hs_jack, rt721->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,
+ &rt721->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 rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL9, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL10, 0x2a00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL6, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL5, 0x2626);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL8, 0x1e00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL7, 0x1515);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL3, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL4, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_CTL1, 0x0000);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ regmap_write(rt721->regmap, 0x2f5c, 0x25);
+}
+
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL6, 0x5561);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_GPIO_PAD_CTRL5, 0x8003);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+}
+
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
+{
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x8011);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL3, 0xcf00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL4, 0x000f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL1, 0x1100);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL5, 0x0c12);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+ RT721_JD_1PIN_GAT_CTRL2, 0xc002);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3333);
+ regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
+ regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL1, 0x0140);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL18, 0x5522);
+ regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
+ regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL4, 0xa105);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3b33);
+ regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3f33);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6048);
+ regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+ regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+ regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON17, 0x0008);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL3, 0x55ff);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL4, 0xcc00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_MBIAS_LV_CTRL2, 0x6677);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF2_LV_CTRL1, 0x7600);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL2, 0x1234);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL3, 0x3512);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL1, 0x4040);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL4, 0x1201);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL,
+ RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a);
+ regmap_write(rt721->regmap, 0x2f58, 0x07);
+}
+
+static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721)
+{
+ mutex_lock(&rt721->calibrate_mutex);
+ if (rt721->hs_jack) {
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ dev_dbg(&rt721->slave->dev, "in %s enable\n", __func__);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_UAJ_CTL, 0x036E);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU03,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU0D,
+ RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_XU_REL_CTRL, 0x0000);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x4000, 0x4000);
+ }
+ mutex_unlock(&rt721->calibrate_mutex);
+}
+
+static int rt721_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721->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;
+ }
+
+ rt721_sdca_jack_init(rt721);
+
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt721_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 rt721_sdca_priv *rt721 = 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 tendA = 0x200;
+ 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(rt721->mbq_regmap, mc->reg, &lvalue);
+ regmap_read(rt721->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 if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_l_val == 0)
+ gain_l_val = 0x8000;
+ else
+ gain_l_val = (gain_l_val - 1) * tendA;
+ } 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 if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (gain_r_val == 0)
+ gain_r_val = 0x8000;
+ else
+ gain_r_val = (gain_r_val - 1) * tendA;
+ } 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(rt721->mbq_regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt721->mbq_regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+ if (read_r == gain_r_val && read_l == gain_l_val)
+ return changed;
+
+ return -EIO;
+}
+
+static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = 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 tendA = 0x200;
+ 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(rt721->mbq_regmap, mc->reg, &read_l);
+ regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+
+ if (mc->shift == 8) {
+ /* boost gain */
+ ctl_l = read_l / tendB;
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_l == 0x8000 || read_l == 0xfe00)
+ ctl_l = 0;
+ else
+ ctl_l = read_l / tendA + 1;
+ } 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 if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_r == 0x8000 || read_r == 0xfe00)
+ ctl_r = 0;
+ else
+ ctl_r = read_r / tendA + 1;
+ } 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 rt721_sdca_set_fu1e_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt721->fu1e_mixer_mute); i++) {
+ ch_mute = rt721->fu1e_dapm_mute || rt721->fu1e_mixer_mute[i];
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt721->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt721_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt721->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt721->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt721_sdca_set_fu1e_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_set_fu0f_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt721->fu0f_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt721->fu0f_mixer_r_mute;
+ return 0;
+}
+
+static int rt721_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt721->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt721->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt721->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt721->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+ err = rt721_sdca_set_fu0f_capture_ctl(rt721);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt721_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_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 rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_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(rt721->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_dmic_kctrl_priv *p =
+ (struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt721_sdca_priv *rt721 = 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(rt721->mbq_regmap, p->reg_base + i, &regvalue[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(rt721->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt721->slave->dev, "%#08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+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 DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0);
+
+static const struct snd_kcontrol_new rt721_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* Headset mic capture settings */
+ SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put),
+ SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic_vol_tlv),
+ SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ /* DMIC capture settings */
+ RT_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4, rt721_sdca_fu_info,
+ rt721_sdca_fu1e_capture_get, rt721_sdca_fu1e_capture_put),
+ RT_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+ RT721_SDCA_CTL_FU_VOLUME, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 0x3f, mic_vol_tlv, rt721_sdca_fu_info),
+ RT_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15,
+ RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+ 4, 3, boost_vol_tlv, rt721_sdca_fu_info),
+};
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft, mask;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & mask;
+
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = 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, mask;
+ unsigned int check;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+ mask_sft = 12;
+ mask = 0x7;
+ } else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+ mask_sft = 10;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+ mask_sft = 8;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+ mask_sft = 6;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+ mask_sft = 4;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+ mask_sft = 2;
+ mask = 0x3;
+ } else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+ mask_sft = 0;
+ mask = 0x3;
+ } else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &val2);
+
+ if (strstr(ucontrol->id.name, "ADC 09 Mux"))
+ val2 = (val2 >> mask_sft) & 0x7;
+ else
+ val2 = (val2 >> mask_sft) & 0x3;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change) {
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, &check);
+ rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_MUX_CTL0, mask << mask_sft,
+ val << mask_sft);
+ }
+
+ snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ item[0], e, NULL);
+
+ return change;
+}
+
+static const char * const adc09_mux_text[] = {
+ "MIC2",
+ "LINE1",
+ "LINE2",
+};
+static const char * const adc07_10_mux_text[] = {
+ "DMIC1 RE",
+ "DMIC1 FE",
+ "DMIC2 RE",
+ "DMIC2 FE",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt721_adc09_enum, SND_SOC_NOPM, 0, adc09_mux_text);
+static SOC_ENUM_SINGLE_DECL(
+ rt721_dmic_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt721_sdca_adc09_mux =
+ SOC_DAPM_ENUM_EXT("ADC 09 Mux", rt721_adc09_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 08 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 10 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_r_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 R Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_l_mux =
+ SOC_DAPM_ENUM_EXT("ADC 07 L Mux", rt721_dmic_enum,
+ rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ msleep(100);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_fu23_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char unmute = 0x0, mute = 0x1;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu1e_dapm_mute = false;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu1e_dapm_mute = true;
+ rt721_sdca_set_fu1e_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt721->fu0f_dapm_mute = false;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt721->fu0f_dapm_mute = true;
+ rt721_sdca_set_fu0f_capture_ctl(rt721);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde41_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static int rt721_sdca_pde34_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+ RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt721_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 41", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde41_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde47_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0,
+ rt721_sdca_pde34_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 23", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_sdca_fu23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+ rt721_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,
+ rt721_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,
+ rt721_sdca_fu113_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 09 Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc09_mux),
+ SND_SOC_DAPM_MUX("ADC 08 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_r_mux),
+ SND_SOC_DAPM_MUX("ADC 08 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc08_l_mux),
+ SND_SOC_DAPM_MUX("ADC 10 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_r_mux),
+ SND_SOC_DAPM_MUX("ADC 10 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc10_l_mux),
+ SND_SOC_DAPM_MUX("ADC 07 R Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_r_mux),
+ SND_SOC_DAPM_MUX("ADC 07 L Mux", SND_SOC_NOPM, 0, 0,
+ &rt721_sdca_adc07_l_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 rt721_sdca_audio_map[] = {
+ {"FU 42", NULL, "DP1RX"},
+ {"FU 21", NULL, "DP3RX"},
+ {"FU 23", NULL, "DP3RX"},
+
+ {"ADC 09 Mux", "MIC2", "MIC2"},
+ {"ADC 09 Mux", "LINE1", "LINE1"},
+ {"ADC 09 Mux", "LINE2", "LINE2"},
+ {"ADC 07 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 07 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 07 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 08 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 08 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 R Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 R Mux", "DMIC2 FE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC1 RE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC1 FE", "DMIC1_2"},
+ {"ADC 10 L Mux", "DMIC2 RE", "DMIC3_4"},
+ {"ADC 10 L Mux", "DMIC2 FE", "DMIC3_4"},
+ {"FU 36", NULL, "PDE 34"},
+ {"FU 36", NULL, "ADC 09 Mux"},
+ {"FU 113", NULL, "PDE 11"},
+ {"FU 113", NULL, "ADC 07 R Mux"},
+ {"FU 113", NULL, "ADC 07 L Mux"},
+ {"FU 113", NULL, "ADC 10 R Mux"},
+ {"FU 113", NULL, "ADC 10 L Mux"},
+ {"DP2TX", NULL, "FU 36"},
+ {"DP6TX", NULL, "FU 113"},
+
+ {"HP", NULL, "PDE 47"},
+ {"HP", NULL, "FU 42"},
+ {"SPK", NULL, "PDE 41"},
+ {"SPK", NULL, "FU 21"},
+ {"SPK", NULL, "FU 23"},
+};
+
+static int rt721_sdca_parse_dt(struct rt721_sdca_priv *rt721, struct device *dev)
+{
+ device_property_read_u32(dev, "realtek,jd-src", &rt721->jd_src);
+
+ return 0;
+}
+
+static int rt721_sdca_probe(struct snd_soc_component *component)
+{
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt721_sdca_parse_dt(rt721, &rt721->slave->dev);
+ rt721->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_rt721 = {
+ .probe = rt721_sdca_probe,
+ .controls = rt721_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt721_sdca_controls),
+ .dapm_widgets = rt721_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt721_sdca_dapm_widgets),
+ .dapm_routes = rt721_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt721_sdca_audio_map),
+ .set_jack = rt721_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+static int rt721_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 rt721_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt721_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 rt721_sdca_priv *rt721 = 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 (!rt721->slave)
+ return -EINVAL;
+
+ /*
+ * RT721_AIF1 with port = 1 for headphone playback
+ * RT721_AIF1 with port = 2 for headset-mic capture
+ * RT721_AIF2 with port = 3 for speaker playback
+ * RT721_AIF3 with port = 6 for digital-mic capture
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ if (dai->id == RT721_AIF1)
+ port = 1;
+ else if (dai->id == RT721_AIF2)
+ port = 3;
+ else
+ return -EINVAL;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ if (dai->id == RT721_AIF1)
+ port = 2;
+ else if (dai->id == RT721_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(rt721->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 8000:
+ sampling_rate = RT721_SDCA_RATE_8000HZ;
+ break;
+ case 16000:
+ sampling_rate = RT721_SDCA_RATE_16000HZ;
+ break;
+ case 24000:
+ sampling_rate = RT721_SDCA_RATE_24000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT721_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT721_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT721_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT721_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT721_SDCA_RATE_192000HZ;
+ break;
+ case 384000:
+ sampling_rate = RT721_SDCA_RATE_384000HZ;
+ break;
+ case 768000:
+ sampling_rate = RT721_SDCA_RATE_768000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ if (dai->id == RT721_AIF1) {
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+ }
+
+ if (dai->id == RT721_AIF2)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ if (dai->id == RT721_AIF3)
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+ RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+ return 0;
+}
+
+static int rt721_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt721->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt721->slave, sdw_stream);
+ return 0;
+}
+
+#define RT721_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT721_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt721_sdca_ops = {
+ .hw_params = rt721_sdca_pcm_hw_params,
+ .hw_free = rt721_sdca_pcm_hw_free,
+ .set_stream = rt721_sdca_set_sdw_stream,
+ .shutdown = rt721_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt721_sdca_dai[] = {
+ {
+ .name = "rt721-sdca-aif1",
+ .id = RT721_AIF1,
+ .playback = {
+ .stream_name = "DP1 Headphone Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif2",
+ .id = RT721_AIF2,
+ .playback = {
+ .stream_name = "DP3 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ },
+ {
+ .name = "rt721-sdca-aif3",
+ .id = RT721_AIF3,
+ .capture = {
+ .stream_name = "DP6 DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT721_STEREO_RATES,
+ .formats = RT721_FORMATS,
+ },
+ .ops = &rt721_sdca_ops,
+ }
+};
+
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721;
+
+ rt721 = devm_kzalloc(dev, sizeof(*rt721), GFP_KERNEL);
+ if (!rt721)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt721);
+ rt721->slave = slave;
+ rt721->regmap = regmap;
+ rt721->mbq_regmap = mbq_regmap;
+
+ regcache_cache_only(rt721->regmap, true);
+ regcache_cache_only(rt721->mbq_regmap, true);
+
+ mutex_init(&rt721->calibrate_mutex);
+ mutex_init(&rt721->disable_irq_lock);
+
+ INIT_DELAYED_WORK(&rt721->jack_detect_work, rt721_sdca_jack_detect_handler);
+ INIT_DELAYED_WORK(&rt721->jack_btn_check_work, rt721_sdca_btn_check_handler);
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt721->hw_init = false;
+ rt721->first_hw_init = false;
+ rt721->fu1e_dapm_mute = true;
+ rt721->fu0f_dapm_mute = true;
+ rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true;
+ rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] =
+ rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true;
+
+ return devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
+}
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+ rt721->disable_irq = false;
+
+ if (rt721->hw_init)
+ return 0;
+
+ regcache_cache_only(rt721->regmap, false);
+ regcache_cache_only(rt721->mbq_regmap, false);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, true);
+ regcache_cache_bypass(rt721->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);
+ rt721_sdca_dmic_preset(rt721);
+ rt721_sdca_amp_preset(rt721);
+ rt721_sdca_jack_preset(rt721);
+ if (rt721->first_hw_init) {
+ regcache_cache_bypass(rt721->regmap, false);
+ regcache_mark_dirty(rt721->regmap);
+ regcache_cache_bypass(rt721->mbq_regmap, false);
+ regcache_mark_dirty(rt721->mbq_regmap);
+ } else
+ rt721->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt721->hw_init = true;
+
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+ return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h
new file mode 100644
index 000000000000..71fac9cd8739
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.h
@@ -0,0 +1,273 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_H__
+#define __RT721_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt721_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 rt721_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* NID */
+#define RT721_ANA_POW_PART 0x01
+#define RT721_DAC_CTRL 0x04
+#define RT721_JD_CTRL 0x09
+#define RT721_CBJ_CTRL 0x0a
+#define RT721_CAP_PORT_CTRL 0x0c
+#define RT721_CLASD_AMP_CTRL 0x0d
+#define RT721_BOOST_CTRL 0x0f
+#define RT721_VENDOR_REG 0x20
+#define RT721_RC_CALIB_CTRL 0x40
+#define RT721_VENDOR_EQ_L 0x53
+#define RT721_VENDOR_EQ_R 0x54
+#define RT721_VENDOR_HP_CALI 0x56
+#define RT721_VENDOR_CHARGE_PUMP 0x57
+#define RT721_VENDOR_CLASD_CALI 0x58
+#define RT721_VENDOR_IMS_DRE 0x5b
+#define RT721_VENDOR_SPK_EFUSE 0x5c
+#define RT721_VENDOR_LEVEL_CTRL 0x5d
+#define RT721_VENDOR_ANA_CTL 0x5f
+#define RT721_HDA_SDCA_FLOAT 0x61
+
+/* Index (NID:01h) */
+#define RT721_MBIAS_LV_CTRL2 0x07
+#define RT721_VREF1_HV_CTRL1 0x0a
+#define RT721_VREF2_LV_CTRL1 0x0b
+
+/* Index (NID:04h) */
+#define RT721_DAC_2CH_CTRL3 0x02
+#define RT721_DAC_2CH_CTRL4 0x03
+
+/* Index (NID:09h) */
+#define RT721_JD_1PIN_GAT_CTRL2 0x07
+
+/* Index (NID:0ah) */
+#define RT721_CBJ_A0_GAT_CTRL1 0x04
+#define RT721_CBJ_A0_GAT_CTRL2 0x05
+
+/* Index (NID:0Ch) */
+#define RT721_HP_AMP_2CH_CAL1 0x05
+#define RT721_HP_AMP_2CH_CAL4 0x08
+#define RT721_HP_AMP_2CH_CAL18 0x1b
+
+/* Index (NID:0dh) */
+#define RT721_CLASD_AMP_2CH_CAL 0x14
+
+/* Index (NID:0fh) */
+#define RT721_BST_4CH_TOP_GATING_CTRL1 0x05
+
+/* Index (NID:20h) */
+#define RT721_JD_PRODUCT_NUM 0x00
+#define RT721_ANALOG_BIAS_CTL3 0x04
+#define RT721_JD_CTRL1 0x09
+#define RT721_LDO2_3_CTL1 0x0e
+#define RT721_GPIO_PAD_CTRL5 0x13
+#define RT721_LDO1_CTL 0x1a
+#define RT721_HP_JD_CTRL 0x24
+#define RT721_VD_HIDDEN_CTRL 0x26
+#define RT721_CLSD_CTRL6 0x3c
+#define RT721_COMBO_JACK_AUTO_CTL1 0x45
+#define RT721_COMBO_JACK_AUTO_CTL2 0x46
+#define RT721_COMBO_JACK_AUTO_CTL3 0x47
+#define RT721_DIGITAL_MISC_CTRL4 0x4a
+#define RT721_VREFO_GAT 0x63
+#define RT721_FSM_CTL 0x67
+#define RT721_SDCA_INTR_REC 0x82
+#define RT721_SW_CONFIG1 0x8a
+#define RT721_SW_CONFIG2 0x8b
+
+/* Index (NID:40h) */
+#define RT721_RC_CALIB_CTRL0 0x00
+
+/* Index (NID:58h) */
+#define RT721_DAC_DC_CALI_CTL1 0x01
+#define RT721_DAC_DC_CALI_CTL2 0x02
+#define RT721_DAC_DC_CALI_CTL3 0x03
+
+/* Index (NID:5fh) */
+#define RT721_MISC_POWER_CTL0 0x00
+#define RT721_MISC_POWER_CTL31 0x31
+#define RT721_UAJ_TOP_TCON13 0x44
+#define RT721_UAJ_TOP_TCON14 0x45
+#define RT721_UAJ_TOP_TCON17 0x48
+
+/* Index (NID:61h) */
+#define RT721_HDA_LEGACY_MUX_CTL0 0x00
+#define RT721_HDA_LEGACY_UAJ_CTL 0x02
+#define RT721_HDA_LEGACY_CTL1 0x05
+#define RT721_HDA_LEGACY_RESET_CTL 0x06
+#define RT721_XU_REL_CTRL 0x0c
+#define RT721_GE_REL_CTRL1 0x0d
+#define RT721_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
+#define RT721_GE_SDCA_RST_CTRL 0x10
+#define RT721_INT_RST_EN_CTRL 0x11
+#define RT721_XU_EVENT_EN 0x13
+#define RT721_INLINE_CTL2 0x17
+#define RT721_UMP_HID_CTRL1 0x18
+#define RT721_UMP_HID_CTRL2 0x19
+#define RT721_UMP_HID_CTRL3 0x1a
+#define RT721_UMP_HID_CTRL4 0x1b
+#define RT721_UMP_HID_CTRL5 0x1c
+#define RT721_FUNC_FLOAT_CTL0 0x22
+#define RT721_FUNC_FLOAT_CTL1 0x23
+#define RT721_FUNC_FLOAT_CTL2 0x24
+#define RT721_FUNC_FLOAT_CTL3 0x25
+#define RT721_ENT_FLOAT_CTL0 0x29
+#define RT721_ENT_FLOAT_CTL1 0x2c
+#define RT721_ENT_FLOAT_CTL2 0x2d
+#define RT721_ENT_FLOAT_CTL3 0x2e
+#define RT721_ENT_FLOAT_CTL4 0x2f
+#define RT721_CH_FLOAT_CTL1 0x45
+#define RT721_CH_FLOAT_CTL2 0x46
+#define RT721_ENT_FLOAT_CTL5 0x53
+#define RT721_ENT_FLOAT_CTL6 0x54
+#define RT721_ENT_FLOAT_CTL7 0x55
+#define RT721_ENT_FLOAT_CTL8 0x57
+#define RT721_ENT_FLOAT_CTL9 0x5a
+#define RT721_ENT_FLOAT_CTL10 0x5b
+#define RT721_CH_FLOAT_CTL3 0x6a
+#define RT721_CH_FLOAT_CTL4 0x6d
+#define RT721_CH_FLOAT_CTL5 0x70
+#define RT721_CH_FLOAT_CTL6 0x92
+
+/* Parameter & Verb control 01 (0x26)(NID:20h) */
+#define RT721_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* Buffer address for HID */
+#define RT721_BUF_ADDR_HID1 0x44030000
+#define RT721_BUF_ADDR_HID2 0x44030020
+
+/* RT721 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
+
+/* RT721 SDCA entity */
+#define RT721_SDCA_ENT_HID01 0x01
+#define RT721_SDCA_ENT_XUV 0x03
+#define RT721_SDCA_ENT_GE49 0x49
+#define RT721_SDCA_ENT_USER_FU05 0x05
+#define RT721_SDCA_ENT_USER_FU06 0x06
+#define RT721_SDCA_ENT_USER_FU0F 0x0f
+#define RT721_SDCA_ENT_USER_FU10 0x19
+#define RT721_SDCA_ENT_USER_FU1E 0x1e
+#define RT721_SDCA_ENT_FU15 0x15
+#define RT721_SDCA_ENT_PDE23 0x23
+#define RT721_SDCA_ENT_PDE40 0x40
+#define RT721_SDCA_ENT_PDE41 0x41
+#define RT721_SDCA_ENT_PDE11 0x11
+#define RT721_SDCA_ENT_PDE12 0x12
+#define RT721_SDCA_ENT_PDE2A 0x2a
+#define RT721_SDCA_ENT_CS01 0x01
+#define RT721_SDCA_ENT_CS11 0x11
+#define RT721_SDCA_ENT_CS1F 0x1f
+#define RT721_SDCA_ENT_CS1C 0x1c
+#define RT721_SDCA_ENT_CS31 0x31
+#define RT721_SDCA_ENT_OT23 0x42
+#define RT721_SDCA_ENT_IT26 0x26
+#define RT721_SDCA_ENT_IT09 0x09
+#define RT721_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT721_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT721_SDCA_ENT_XU03 0x03
+#define RT721_SDCA_ENT_XU0D 0x0d
+#define RT721_SDCA_ENT_FU55 0x55
+
+/* RT721 SDCA control */
+#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT721_SDCA_CTL_FU_MUTE 0x01
+#define RT721_SDCA_CTL_FU_VOLUME 0x02
+#define RT721_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT721_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT721_SDCA_CTL_SELECTED_MODE 0x01
+#define RT721_SDCA_CTL_DETECTED_MODE 0x02
+#define RT721_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT721_SDCA_CTL_VENDOR_DEF 0x30
+#define RT721_SDCA_CTL_XUV 0x34
+#define RT721_SDCA_CTL_FU_CH_GAIN 0x0b
+
+/* RT721 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
+#define CH_09 0x09
+#define CH_0A 0x0a
+
+/* sample frequency index */
+#define RT721_SDCA_RATE_8000HZ 0x01
+#define RT721_SDCA_RATE_11025HZ 0x02
+#define RT721_SDCA_RATE_12000HZ 0x03
+#define RT721_SDCA_RATE_16000HZ 0x04
+#define RT721_SDCA_RATE_22050HZ 0x05
+#define RT721_SDCA_RATE_24000HZ 0x06
+#define RT721_SDCA_RATE_32000HZ 0x07
+#define RT721_SDCA_RATE_44100HZ 0x08
+#define RT721_SDCA_RATE_48000HZ 0x09
+#define RT721_SDCA_RATE_88200HZ 0x0a
+#define RT721_SDCA_RATE_96000HZ 0x0b
+#define RT721_SDCA_RATE_176400HZ 0x0c
+#define RT721_SDCA_RATE_192000HZ 0x0d
+#define RT721_SDCA_RATE_384000HZ 0x0e
+#define RT721_SDCA_RATE_768000HZ 0x0f
+
+/* RT721 HID ID */
+#define RT721_SDCA_HID_ID 0x11
+
+enum {
+ RT721_AIF1, /* For headset mic and headphone */
+ RT721_AIF2, /* For speaker */
+ RT721_AIF3, /* For dmic */
+ RT721_AIFS,
+};
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave);
+#endif /* __RT721_H__ */
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
new file mode 100644
index 000000000000..5ea40c1b159a
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt722-sdca.h"
+#include "rt722-sdca-sdw.h"
+
+static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f50 ... 0x2f52:
+ case 0x2f54:
+ case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ 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_JACK_CODEC, RT722_SDCA_ENT_XU03, RT722_SDCA_CTL_SELECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 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 SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ return 1;
+ 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 0x3110000:
+ case 0x5300000 ... 0x5300002:
+ case 0x5400002:
+ case 0x5600000 ... 0x5600007:
+ case 0x5700000 ... 0x5700004:
+ case 0x5800000 ... 0x5800004:
+ case 0x5810000:
+ case 0x5b00003:
+ case 0x5c00011:
+ case 0x5d00006:
+ case 0x5f00000 ... 0x5f0000d:
+ case 0x5f00030:
+ case 0x6100000 ... 0x6100051:
+ case 0x6100055 ... 0x6100057:
+ case 0x6100060:
+ case 0x6100062:
+ case 0x6100064 ... 0x6100065:
+ case 0x6100067:
+ case 0x6100070 ... 0x610007c:
+ case 0x6100080:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN,
+ CH_04):
+ 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 2;
+ default:
+ return 0;
+ }
+}
+
+static const struct regmap_sdw_mbq_cfg rt722_mbq_config = {
+ .mbq_size = rt722_sdca_mbq_size,
+};
+
+static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ return rt722_sdca_mbq_size(dev, reg) > 0;
+}
+
+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_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 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_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 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 SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ case 0x2000000:
+ case 0x200000d:
+ case 0x2000019:
+ case 0x2000020:
+ case 0x2000030:
+ case 0x2000046:
+ case 0x2000067:
+ case 0x2000084:
+ case 0x2000086:
+ case 0x3110000:
+ case 0x5800003:
+ case 0x5810000:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt722_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+ .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 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_0);
+ 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;
+
+ sdw_slave_read_lane_mapping(slave);
+
+ 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 = 900;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ /* Three data lanes are supported by rt722-sdca codec */
+ prop->lane_control_support = true;
+
+ 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;
+ }
+
+ 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(280));
+
+ 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 const 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;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw_mbq_cfg(slave, &rt722_sdca_regmap, &rt722_mbq_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt722_sdca_init(&slave->dev, 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 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);
+
+ return 0;
+}
+
+static int 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, 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 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) {
+ mutex_lock(&rt722->disable_irq_lock);
+ if (rt722->disable_irq == true) {
+ 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);
+ 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);
+ return 0;
+}
+
+static const struct dev_pm_ops rt722_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume)
+ RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt722_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt722-sdca",
+ .pm = pm_ptr(&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 <jack.yu@realtek.com>");
+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 000000000000..c5dd472a2c00
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -0,0 +1,122 @@
+/* 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 <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+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 },
+ { 0x200003c, 0xc214 },
+ { 0x2000046, 0x8004 },
+ { 0x5810000, 0x702d },
+ { 0x6100000, 0x0201 },
+ { 0x6100006, 0x0005 },
+ { 0x6100010, 0x2630 },
+ { 0x6100011, 0x152f },
+ { 0x6100013, 0x0102 },
+ { 0x6100015, 0x2200 },
+ { 0x6100017, 0x0102 },
+ { 0x6100025, 0x2a29 },
+ { 0x6100026, 0x2a00 },
+ { 0x6100028, 0x2a2a },
+ { 0x6100029, 0x4141 },
+ { 0x6100055, 0x0000 },
+ { 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_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_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_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_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { 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_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { 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_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_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_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_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { 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_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0),
+ 0x00 },
+ { 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_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 },
+ { 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_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+};
+
+#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 000000000000..79b8b7e70a33
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt722-sdca.h"
+
+#define RT722_NID_ADDR(nid, reg) ((nid) << 20 | (reg))
+
+int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+ int ret;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to set private value: %06x <= %04x ret=%d\n",
+ __func__, 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->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt722->slave->dev,
+ "%s: Failed to get private value: %06x => %04x ret=%d\n",
+ __func__, 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_0 is used for jack detection */
+ if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+ 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);
+ /* 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);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL1, 0x0000);
+ /* 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_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->regmap, mc->reg, &lvalue);
+ regmap_read(rt722->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->regmap, mc->reg, gain_l_val);
+
+ /* Rch */
+ regmap_write(rt722->regmap, mc->rreg, gain_r_val);
+
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->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->regmap, mc->reg, &read_l);
+ regmap_read(rt722->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->regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / boost_step;
+ else /* ADC gain */
+ ctl = p->max - (((vol_max - 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->regmap, p->reg_base + i, &regvalue[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->regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n",
+ __func__, 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 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,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 12, adc22_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc24_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 4, adc07_10_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt722_adc25_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt722_sdca_adc22_mux =
+ SOC_DAPM_ENUM("ADC 22 Mux", rt722_adc22_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc24_mux =
+ SOC_DAPM_ENUM("ADC 24 Mux", rt722_adc24_enum);
+
+static const struct snd_kcontrol_new rt722_sdca_adc25_mux =
+ SOC_DAPM_ENUM("ADC 25 Mux", rt722_adc25_enum);
+
+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);
+ usleep_range(150000, 160000);
+ 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 void rt722_pde_transition_delay(struct rt722_sdca_priv *rt722, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 1000, val;
+
+ pm_runtime_mark_last_busy(&rt722->slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(func, entity, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt722->slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ }
+}
+
+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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, 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);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, 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, "%s: Unable to configure port\n", __func__);
+ return retval;
+ }
+
+ if (params_channels(params) > 16) {
+ dev_err(component->dev, "%s: Unsupported channels %d\n",
+ __func__, 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, "%s: Rate %d is not supported\n",
+ __func__, 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 = 4,
+ .rates = RT722_STEREO_RATES,
+ .formats = RT722_FORMATS,
+ },
+ .ops = &rt722_sdca_ops,
+ }
+};
+
+int rt722_sdca_init(struct device *dev, struct regmap *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;
+
+ regcache_cache_only(rt722->regmap, true);
+
+ 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)
+{
+ unsigned int mic_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ dev_dbg(dev, "%s mic func_status=0x%x\n", __func__, mic_func_status);
+
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* 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);
+ /* PHYtiming TDZ/TZD control */
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f52, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
+{
+ unsigned int amp_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* 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);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f54, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
+{
+ int loop_check, chk_cnt = 100, ret;
+ unsigned int calib_status = 0;
+ unsigned int jack_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ dev_dbg(dev, "%s jack func_status=0x%x\n", __func__, jack_func_status);
+
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Config analog bias */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3,
+ 0xa081);
+ /* GE related settings */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2,
+ 0xa009);
+ /* 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++) {
+ usleep_range(10000, 11000);
+ ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
+ RT722_DAC_DC_CALI_CTL3, &calib_status);
+ if (ret < 0)
+ dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
+ if ((calib_status & 0x0040) == 0x0)
+ break;
+ }
+
+ if (loop_check == chk_cnt)
+ dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__);
+
+ /* 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);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1,
+ 0x4141);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1,
+ 0x0101);
+ /* Fine tune PDE40 latency */
+ regmap_write(rt722->regmap, 0x2f58, 0x07);
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+ /* MIC VRefo */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200);
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_VREFO_GAT, 0x4000, 0x4000);
+ /* 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);
+
+ if (rt722->hw_vid == RT722_VB)
+ regmap_write(rt722->regmap, 0x2f51, 0x00);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ rt722->disable_irq = false;
+
+ if (rt722->hw_init)
+ return 0;
+
+ regcache_cache_only(rt722->regmap, false);
+ if (rt722->first_hw_init) {
+ regcache_cache_bypass(rt722->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_index_read(rt722, RT722_VENDOR_REG, RT722_JD_PRODUCT_NUM, &val);
+ rt722->hw_vid = (val & 0x0f00) >> 8;
+ dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid);
+
+ 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);
+ } else
+ rt722->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt722->hw_init = true;
+
+ 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 <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
new file mode 100644
index 000000000000..823abee9ab76
--- /dev/null
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -0,0 +1,250 @@
+/* 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 <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct rt722_sdca_priv {
+ struct regmap *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];
+ int hw_vid;
+};
+
+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_VREFO_GAT 0x63
+#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
+#define RT722_FLOAT_CTRL_1 0x70
+#define RT722_ENT_FLOAT_CTRL_1 0x76
+
+/* 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
+#define RT722_SDCA_ENT0 0x00
+
+/* 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
+#define RT722_SDCA_CTL_FUNC_STATUS 0x10
+#define RT722_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+
+/* 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
+
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
+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,
+};
+
+enum rt722_sdca_version {
+ RT722_VA,
+ RT722_VB,
+};
+
+int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt722_sdca_init(struct device *dev, struct regmap *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 000000000000..97f56af25577
--- /dev/null
+++ b/sound/soc/codecs/rt9120.c
@@ -0,0 +1,643 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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 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 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 = {
+ 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 = pm_ptr(&rt9120_pm_ops),
+ },
+ .probe = rt9120_probe,
+ .remove = rt9120_remove,
+};
+module_i2c_driver(rt9120_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RT9120 Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
new file mode 100644
index 000000000000..b162824526d6
--- /dev/null
+++ b/sound/soc/codecs/rt9123.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123.c -- RT9123 (SW I2C Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RT9123_REG_AMPCTRL 0x01
+#define RT9123_REG_I2SOPT 0x02
+#define RT9123_REG_TDMRX 0x03
+#define RT9123_REG_SILVOLEN 0x04
+#define RT9123_REG_VOLGAIN 0x12
+#define RT9123_REG_ANAFLAG 0x36
+#define RT9123_REG_COMBOID 0xF7
+
+#define RT9123_MASK_SWRST BIT(15)
+#define RT9123_MASK_SWMUTE BIT(14)
+#define RT9123_MASK_AMPON BIT(12)
+#define RT9123_MASK_AUDBIT GENMASK(14, 12)
+#define RT9123_MASK_AUDFMT GENMASK(11, 8)
+#define RT9123_MASK_TDMRXLOC GENMASK(4, 0)
+#define RT9123_MASK_VENID GENMASK(15, 4)
+
+#define RT9123_FIXED_VENID 0x340
+
+struct rt9123_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rt9123_enable_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 device *dev = comp->dev;
+ unsigned int enable;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */
+ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-1200, 0, 0),
+ 1, 9, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(1400, 0, 0));
+static const char * const pwmfreq_text[] = { "300KHz", "325KHz", "350KHz", "375KHz" };
+static const struct soc_enum rt9123_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_AMPCTRL, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rt9123_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_I2SOPT, 4, ARRAY_SIZE(i2sch_text), i2sch_text);
+
+static int rt9123_kcontrol_name_comp(struct snd_kcontrol *kcontrol, const char *s)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ const char *kctlname = kcontrol->id.name;
+
+ if (comp && comp->name_prefix)
+ kctlname += strlen(comp->name_prefix) + 1;
+
+ return strcmp(kctlname, s);
+}
+
+static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG read
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to get control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG write
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to put control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static const struct snd_kcontrol_new rt9123_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RT9123_REG_VOLGAIN, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_EXT_TLV("Speaker Volume", RT9123_REG_AMPCTRL, 0, 10, 0, rt9123_xhandler_get,
+ rt9123_xhandler_put, ana_tlv),
+ SOC_ENUM_EXT("PWM Frequency Select", rt9123_pwm_freq_enum, rt9123_xhandler_get,
+ rt9123_xhandler_put),
+ SOC_ENUM("I2S CH Select", rt9123_i2sch_select_enum),
+ SOC_SINGLE("Silence Detect Switch", RT9123_REG_SILVOLEN, 14, 1, 0),
+};
+
+static const struct snd_soc_component_driver rt9123_comp_driver = {
+ .controls = rt9123_controls,
+ .num_controls = ARRAY_SIZE(rt9123_controls),
+ .dapm_widgets = rt9123_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123_dapm_widgets),
+ .dapm_routes = rt9123_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt9123_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+
+ rt9123->dai_fmt = fmt;
+ return 0;
+}
+
+static int rt9123_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int rx_loc;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slots % 2 || slot_width % 8 ||
+ slots * slot_width > 256) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || ffs(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure rx channel data location */
+ rx_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write_field(comp, RT9123_REG_TDMRX, RT9123_MASK_TDMRXLOC, rx_loc);
+
+ rt9123->tdm_slots = slots;
+ rt9123->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rt9123_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rt9123->dai_fmt);
+ if (rt9123->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rt9123->tdm_slots ? 4 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rt9123->tdm_slots ? 12 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ audbit = 2;
+ break;
+ case 32:
+ audbit = 3;
+ break;
+ case 8:
+ audbit = 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+ if (rt9123->tdm_slots && slot_width > rt9123->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDFMT, audfmt);
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDBIT, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9123_dai_ops = {
+ .set_fmt = rt9123_dai_set_format,
+ .set_tdm_slot = rt9123_dai_set_tdm_slot,
+ .hw_params = rt9123_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9123_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_22050 | SNDRV_PCM_RATE_24000 |
+ 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 = &rt9123_dai_ops,
+};
+
+static bool rt9123_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01:
+ case 0x20:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt9123_regmap_config = {
+ .name = "rt9123",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .readable_reg = rt9123_readable_reg,
+ .writeable_reg = rt9123_writeable_reg,
+ .volatile_reg = rt9123_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = RT9123_REG_ANAFLAG + 1,
+};
+
+static int rt9123_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rt9123_priv *rt9123;
+ struct regmap *regmap;
+ __be16 value;
+ u16 venid;
+ int ret;
+
+ rt9123 = devm_kzalloc(dev, sizeof(*rt9123), GFP_KERNEL);
+ if (!rt9123)
+ return -ENOMEM;
+
+ rt9123->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt9123->enable))
+ return PTR_ERR(rt9123->enable);
+ else if (rt9123->enable)
+ usleep_range(250, 350);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_i2c_block_data(i2c, RT9123_REG_COMBOID, sizeof(value), (u8 *)&value);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read vendor-id\n");
+
+ venid = be16_to_cpu(value);
+ if ((venid & RT9123_MASK_VENID) != RT9123_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect vendor-id 0x%04x\n", venid);
+
+ /* Trigger RG reset before regmap init cache */
+ value = cpu_to_be16(RT9123_MASK_SWRST);
+ ret = i2c_smbus_write_i2c_block_data(i2c, RT9123_REG_AMPCTRL, sizeof(value), (u8 *)&value);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init_i2c(i2c, &rt9123_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ i2c_set_clientdata(i2c, rt9123);
+
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rt9123_comp_driver, &rt9123_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rt9123_runtime_suspend(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rt9123->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rt9123->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rt9123_runtime_resume(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rt9123->enable) {
+ gpiod_set_value(rt9123->enable, 1);
+ usleep_range(250, 350);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rt9123_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rt9123_runtime_suspend, rt9123_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123_device_id[] = {
+ { .compatible = "richtek,rt9123" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123_acpi_match[] = {
+ { "RT9123", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123_acpi_match);
+#endif
+
+static struct i2c_driver rt9123_i2c_driver = {
+ .driver = {
+ .name = "rt9123",
+ .of_match_table = of_match_ptr(rt9123_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123_acpi_match),
+ .pm = pm_ptr(&rt9123_dev_pm_ops),
+ },
+ .probe = rt9123_i2c_probe,
+};
+module_i2c_driver(rt9123_i2c_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c
new file mode 100644
index 000000000000..d509659e735b
--- /dev/null
+++ b/sound/soc/codecs/rt9123p.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123p.c -- RT9123 (HW Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt9123p_priv {
+ struct gpio_desc *enable;
+ unsigned int enable_delay;
+ int enable_switch;
+};
+
+static int rt9123p_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (!rt9123p->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mdelay(rt9123p->enable_delay);
+ if (rt9123p->enable_switch) {
+ gpiod_set_value(rt9123p->enable, 1);
+ dev_dbg(comp->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(rt9123p->enable, 0);
+ dev_dbg(comp->dev, "set enable to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt9123p_enable_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 rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ rt9123p->enable_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ rt9123p->enable_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123p_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123p_dapm_routes[] = {
+ {"Amp Drv", NULL, "HiFi Playback"},
+ {"SPK", NULL, "Amp Drv"},
+};
+
+static const struct snd_soc_component_driver rt9123p_comp_driver = {
+ .dapm_widgets = rt9123p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123p_dapm_widgets),
+ .dapm_routes = rt9123p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt9123p_dai_ops = {
+ .trigger = rt9123p_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver rt9123p_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_22050 | SNDRV_PCM_RATE_24000 |
+ 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 = &rt9123p_dai_ops,
+};
+
+static int rt9123p_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rt9123p_priv *rt9123p;
+ int ret;
+
+ rt9123p = devm_kzalloc(dev, sizeof(*rt9123p), GFP_KERNEL);
+ if (!rt9123p)
+ return -ENOMEM;
+
+ rt9123p->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(rt9123p->enable))
+ return PTR_ERR(rt9123p->enable);
+
+ ret = device_property_read_u32(dev, "enable-delay-ms", &rt9123p->enable_delay);
+ if (ret) {
+ rt9123p->enable_delay = 0;
+ dev_dbg(dev, "no optional property 'enable-delay-ms' found, default: no delay\n");
+ }
+
+ platform_set_drvdata(pdev, rt9123p);
+
+ return devm_snd_soc_register_component(dev, &rt9123p_comp_driver, &rt9123p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123p_device_id[] = {
+ { .compatible = "richtek,rt9123p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123p_acpi_match[] = {
+ { "RT9123P", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123p_acpi_match);
+#endif
+
+static struct platform_driver rt9123p_platform_driver = {
+ .driver = {
+ .name = "rt9123p",
+ .of_match_table = of_match_ptr(rt9123p_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123p_acpi_match),
+ },
+ .probe = rt9123p_platform_probe,
+};
+module_platform_driver(rt9123p_platform_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123p Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
new file mode 100644
index 000000000000..186904b31434
--- /dev/null
+++ b/sound/soc/codecs/rtq9124.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rtq9124.c -- RTQ9124 ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RTQ9124_REG_SDI_SEL 0x00
+#define RTQ9124_REG_SDO_SEL 0x01
+#define RTQ9124_REG_I2S_OPT 0x02
+#define RTQ9124_REG_AMP_OPT 0x03
+#define RTQ9124_REG_STATE_CTRL 0x04
+#define RTQ9124_REG_PWM_PHASE 0x05
+#define RTQ9124_REG_SIL_CTRL 0x06
+#define RTQ9124_REG_PWM_SS_OPT 0x07
+#define RTQ9124_REG_ERR_INT_0 0x10
+#define RTQ9124_REG_ERR_MASK6 0x26
+#define RTQ9124_REG_TDM_TX_CH0 0x32
+#define RTQ9124_REG_TDM_RX_CH0 0x34
+#define RTQ9124_REG_VOL_OPT 0x38
+#define RTQ9124_REG_DCR_TH 0x4B
+#define RTQ9124_REG_ERR_TH 0x4C
+#define RTQ9124_REG_PROT_EN 0x5B
+#define RTQ9124_REG_PRJ_CODE 0xF9
+
+#define RTQ9124_MASK_CS_DATA_INV BIT(9)
+#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8)
+#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4)
+#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0)
+#define RTQ9124_MASK_CH_STATE GENMASK(1, 0)
+#define RTQ9124_MASK_SF_RESET BIT(15)
+
+#define RTQ9124_FIXED_VENID 0x9124
+
+struct rtq9124_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rtq9124_enable_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);
+ unsigned int i, chan_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Change state to normal */
+ chan_state = 0;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Change state to HiZ */
+ chan_state = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Before amp turn on, clear old events first */
+ for (i = 0; !chan_state && i < 8; i++)
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff);
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE,
+ chan_state);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0));
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rtq9124_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text);
+static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" };
+static const struct soc_enum rtq9124_sdo_vselect_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text);
+static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" };
+static const struct soc_enum rtq9124_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" };
+static const struct soc_enum rtq9124_out_angle_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text);
+static const char * const sdo_select_text[] = {
+ "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin",
+ "DCR",
+};
+static const struct soc_enum rtq9124_sdo_select_enum =
+ SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text);
+static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" };
+static const struct soc_enum rtq9124_ulqm_dcvt_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text);
+
+static const struct snd_kcontrol_new rtq9124_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv),
+ SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum),
+ SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum),
+ SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum),
+ SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum),
+ SOC_ENUM("SDO Select", rtq9124_sdo_select_enum),
+ SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum),
+ SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0),
+ SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0),
+};
+
+static int rtq9124_comp_probe(struct snd_soc_component *comp)
+{
+ /* CS Data INV */
+ snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1);
+
+ /* RTLD */
+ snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff);
+ snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9124_comp_driver = {
+ .probe = rtq9124_comp_probe,
+ .controls = rtq9124_controls,
+ .num_controls = ARRAY_SIZE(rtq9124_controls),
+ .dapm_widgets = rtq9124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets),
+ .dapm_routes = rtq9124_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+
+ rtq9124->dai_fmt = fmt;
+ return 0;
+}
+
+static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int byte_loc, i;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) {
+ dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure tx channel data location */
+ for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) {
+ byte_loc = (ffs(tx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc);
+ }
+
+ /* Configure rx channel data location */
+ byte_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc);
+
+ rtq9124->tdm_slots = slots;
+ rtq9124->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width, bitrate;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt);
+ if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A &&
+ fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rtq9124->tdm_slots ? 7 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rtq9124->tdm_slots ? 15 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ if (rtq9124->tdm_slots) {
+ slot_width = params_physical_width(param);
+ if (slot_width > rtq9124->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate);
+ return -EINVAL;
+ }
+ }
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt);
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rtq9124_dai_ops = {
+ .set_fmt = rtq9124_dai_set_format,
+ .set_tdm_slot = rtq9124_dai_set_tdm_slot,
+ .hw_params = rtq9124_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rtq9124_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_48000 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rtq9124_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static bool rtq9124_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30 ... 0x3D:
+ case 0x40 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x09:
+ case 0x0C ... 0x0E:
+ case 0x10 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30:
+ case 0x32 ... 0x3D:
+ case 0x40 ... 0x4E:
+ case 0x50 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0A ... 0x0B:
+ case 0x0F ... 0x17:
+ case 0x31:
+ case 0x4F:
+ case 0x51:
+ case 0x53 ... 0x57:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline u8 rtq9124_get_reg_len(unsigned int reg)
+{
+ return (reg >= 0x40 && reg <= 0x47) ? 4 : 2;
+}
+
+static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)reg_buf;
+ u8 size = rtq9124_get_reg_len(reg);
+ u32 *val = val_buf;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf);
+
+ return 0;
+}
+
+static int rtq9124_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)data, *vbuf;
+ u8 size = rtq9124_get_reg_len(reg);
+ __be16 val16 = cpu_to_be16p(data + 1);
+ __be32 val32 = cpu_to_be32p(data + 1);
+
+ vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16;
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf);
+}
+
+static const struct regmap_config rtq9124_regmap_config = {
+ .name = "rtq9124",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .read = rtq9124_regmap_read,
+ .write = rtq9124_regmap_write,
+ .readable_reg = rtq9124_readable_reg,
+ .writeable_reg = rtq9124_writeable_reg,
+ .volatile_reg = rtq9124_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = 0xFD + 1,
+ .use_single_read = 1,
+ .use_single_write = 1,
+};
+
+static const struct reg_sequence rtq9124_init_regs[] = {
+ { 0xfb, 0x0065 },
+ { 0x93, 0x2000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rtq9124_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9124_priv *rtq9124;
+ struct regmap *regmap;
+ int ret;
+
+ rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL);
+ if (!rtq9124)
+ return -ENOMEM;
+
+ rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rtq9124->enable))
+ return PTR_ERR(rtq9124->enable);
+ else if (rtq9124->enable)
+ usleep_range(6000, 7000);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read project code\n");
+ else if (ret != RTQ9124_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret);
+
+ /* Trigger RG reset before regmap init */
+ ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register regmap patch\n");
+
+ i2c_set_clientdata(i2c, rtq9124);
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rtq9124_runtime_suspend(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rtq9124->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rtq9124->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rtq9124_runtime_resume(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rtq9124->enable) {
+ gpiod_set_value(rtq9124->enable, 1);
+ usleep_range(6000, 7000);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rtq9124_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtq9124_device_id[] = {
+ { .compatible = "richtek,rtq9124" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9124_device_id);
+#endif
+
+static struct i2c_driver rtq9124_driver = {
+ .driver = {
+ .name = "rtq9124",
+ .of_match_table = of_match_ptr(rtq9124_device_id),
+ .pm = pm_ptr(&rtq9124_dev_pm_ops),
+ },
+ .probe = rtq9124_probe,
+};
+module_i2c_driver(rtq9124_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC RTQ9124 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
new file mode 100644
index 000000000000..391cc03d687f
--- /dev/null
+++ b/sound/soc/codecs/rtq9128.c
@@ -0,0 +1,789 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright (c) 2023 Richtek Technology Corp.
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+//
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define RTQ9128_REG_SDI_SEL 0x00
+#define RTQ9128_REG_SDO_SEL 0x01
+#define RTQ9128_REG_I2S_OPT 0x02
+#define RTQ9128_REG_MISC 0x03
+#define RTQ9128_REG_STATE_CTRL 0x04
+#define RTQ9128_REG_PLLTRI_GEN1 0x05
+#define RTQ9128_REG_PLLTRI_GEN2 0x06
+#define RTQ9128_REG_PWM_SS_OPT 0x07
+#define RTQ9128_REG_DSP_EN 0x08
+#define RTQ9128_REG_TDM_TX_CH1 0x21
+#define RTQ9128_REG_TDM_RX_CH1 0x25
+#define RTQ9128_REG_MS_VOL 0x30
+#define RTQ9128_REG_CH1_VOL 0x31
+#define RTQ9128_REG_CH2_VOL 0x32
+#define RTQ9128_REG_CH3_VOL 0x33
+#define RTQ9128_REG_CH4_VOL 0x34
+#define RTQ9128_REG_PROT_OPT 0x71
+#define RTQ9128_REG_EFUSE_DATA 0xE0
+#define RTQ9128_REG_VENDOR_ID 0xF9
+
+#define RTQ9128_CHSTAT_VAL_MASK GENMASK(1, 0)
+#define RTQ9128_DOLEN_MASK GENMASK(7, 6)
+#define RTQ9128_TDMSRCIN_MASK GENMASK(5, 4)
+#define RTQ9128_AUDBIT_MASK GENMASK(5, 4)
+#define RTQ9128_AUDFMT_MASK GENMASK(3, 0)
+#define RTQ9128_MSMUTE_MASK BIT(0)
+#define RTQ9128_DIE_CHECK_MASK GENMASK(4, 0)
+#define RTQ9128_VENDOR_ID_MASK GENMASK(19, 8)
+
+#define RTQ9128_SOFT_RESET_VAL 0x80
+#define RTQ9128_VENDOR_ID_VAL 0x470
+#define RTQ9128_ALLCH_HIZ_VAL 0x55
+#define RTQ9128_ALLCH_ULQM_VAL 0xFF
+#define RTQ9128_TKA470B_VAL 0
+#define RTQ9128_RTQ9128DH_VAL 0x0F
+#define RTQ9128_RTQ9128DL_VAL 0x10
+
+struct rtq9128_data {
+ struct gpio_desc *enable;
+ unsigned int daifmt;
+ int tdm_slots;
+ int tdm_slot_width;
+ bool tdm_input_data2_select;
+};
+
+struct rtq9128_init_reg {
+ unsigned int reg;
+ unsigned int val;
+};
+
+static int rtq9128_get_reg_size(unsigned int reg)
+{
+ switch (reg) {
+ case 0x5C ... 0x6F:
+ case 0x98 ... 0x9F:
+ case 0xC0 ... 0xC3:
+ case 0xC8 ... 0xCF:
+ case 0xDF ... 0xE5:
+ case 0xF9:
+ return 4;
+ case 0x40 ... 0x4F:
+ return 3;
+ case 0x30 ... 0x35:
+ case 0x8C ... 0x97:
+ case 0xC4 ... 0xC7:
+ case 0xD7 ... 0xDA:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static int rtq9128_i2c_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)data;
+ int rg_size;
+
+ if (count != 5) {
+ dev_err(dev, "Invalid write for data length (%d)\n", (int)count);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ return i2c_smbus_write_i2c_block_data(i2c, reg, rg_size, data + count - rg_size);
+}
+
+static int rtq9128_i2c_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ u8 reg = *(u8 *)reg_buf;
+ u8 data_tmp[4] = {};
+ int rg_size, ret;
+
+ if (reg_size != 1 || val_size != 4) {
+ dev_err(dev, "Invalid read for reg_size (%d) or val_size (%d)\n", (int)reg_size,
+ (int)val_size);
+ return -EINVAL;
+ }
+
+ rg_size = rtq9128_get_reg_size(reg);
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, rg_size, data_tmp);
+ if (ret < 0)
+ return ret;
+ else if (ret != rg_size)
+ return -EIO;
+
+ memset(val_buf, 0, val_size - rg_size);
+ memcpy(val_buf + val_size - rg_size, data_tmp, rg_size);
+
+ return 0;
+}
+
+static const struct regmap_bus rtq9128_regmap_bus = {
+ .write = rtq9128_i2c_write,
+ .read = rtq9128_i2c_read,
+ .max_raw_read = 4,
+ .max_raw_write = 4,
+};
+
+static bool rtq9128_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0 ... 0xE5:
+ case 0xF0 ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x1F:
+ case 0x21 ... 0x2B:
+ case 0x30 ... 0x35:
+ case 0x40 ... 0x56:
+ case 0x5C ... 0x76:
+ case 0x80 ... 0x8B:
+ case 0xA0 ... 0xAD:
+ case 0xB0 ... 0xBA:
+ case 0xC0:
+ case 0xD0 ... 0xDE:
+ case 0xE0 ... 0xE5:
+ case 0xF0 ... 0xF3:
+ case 0xF6 ... 0xF8:
+ case 0xFA ... 0xFB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9128_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0F ... 0x17:
+ case 0x20:
+ case 0x53:
+ case 0x55:
+ case 0x5C ... 0x6F:
+ case 0x8C ... 0x9F:
+ case 0xC0 ... 0xCF:
+ case 0xDF:
+ case 0xF0 ... 0xF1:
+ case 0xF4 ... 0xF5:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rtq9128_regmap_config = {
+ .name = "rtq9128",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .cache_type = REGCACHE_MAPLE,
+
+ .readable_reg = rtq9128_is_readable_reg,
+ .writeable_reg = rtq9128_is_writeable_reg,
+ .volatile_reg = rtq9128_is_volatile_reg,
+ .num_reg_defaults_raw = RTQ9128_REG_VENDOR_ID + 1,
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+
+static const DECLARE_TLV_DB_RANGE(spkgain_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 5, TLV_DB_SCALE_ITEM(1500, 300, 0),
+);
+
+static const char * const source_select_text[] = { "CH1", "CH2", "CH3", "CH4" };
+static const char * const pwmfreq_select_text[] = { "8fs", "10fs", "40fs", "44fs", "48fs" };
+static const char * const phase_select_text[] = {
+ "0 degree", "45 degree", "90 degree", "135 degree",
+ "180 degree", "225 degree", "270 degree", "315 degree",
+};
+static const char * const dvdduv_select_text[] = { "1P4V", "1P5V", "2P1V", "2P3V" };
+
+static const struct soc_enum rtq9128_ch1_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 6, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch2_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 4, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch3_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 2, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_ch4_si_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_SDI_SEL, 0, ARRAY_SIZE(source_select_text), source_select_text);
+static const struct soc_enum rtq9128_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 4, ARRAY_SIZE(pwmfreq_select_text),
+ pwmfreq_select_text);
+static const struct soc_enum rtq9128_out2_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN1, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out3_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 4, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+static const struct soc_enum rtq9128_out4_phase_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PLLTRI_GEN2, 0, ARRAY_SIZE(phase_select_text),
+ phase_select_text);
+
+/*
+ * In general usage, DVDD could be 1P8V, 3P0V or 3P3V.
+ * This DVDD undervoltage protection is to prevent from the abnormal power
+ * lose case while the amplifier is operating. Due to the different DVDD
+ * application, treat this threshold as a user choosable option.
+ */
+static const struct soc_enum rtq9128_dvdduv_select_enum =
+ SOC_ENUM_SINGLE(RTQ9128_REG_PROT_OPT, 6, ARRAY_SIZE(dvdduv_select_text),
+ dvdduv_select_text);
+
+static const struct snd_kcontrol_new rtq9128_snd_ctrls[] = {
+ SOC_SINGLE_TLV("MS Volume", RTQ9128_REG_MS_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH1 Volume", RTQ9128_REG_CH1_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH2 Volume", RTQ9128_REG_CH2_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH3 Volume", RTQ9128_REG_CH3_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("CH4 Volume", RTQ9128_REG_CH4_VOL, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("SPK Gain Volume", RTQ9128_REG_MISC, 0, 5, 0, spkgain_tlv),
+ SOC_SINGLE("PBTL12 Switch", RTQ9128_REG_MISC, 5, 1, 0),
+ SOC_SINGLE("PBTL34 Switch", RTQ9128_REG_MISC, 4, 1, 0),
+ SOC_SINGLE("Spread Spectrum Switch", RTQ9128_REG_PWM_SS_OPT, 7, 1, 0),
+ SOC_SINGLE("SDO Select", RTQ9128_REG_SDO_SEL, 0, 15, 0),
+ SOC_ENUM("CH1 SI Select", rtq9128_ch1_si_enum),
+ SOC_ENUM("CH2 SI Select", rtq9128_ch2_si_enum),
+ SOC_ENUM("CH3 SI Select", rtq9128_ch3_si_enum),
+ SOC_ENUM("CH4 SI Select", rtq9128_ch4_si_enum),
+ SOC_ENUM("PWM FREQ Select", rtq9128_pwm_freq_enum),
+ SOC_ENUM("OUT2 Phase Select", rtq9128_out2_phase_enum),
+ SOC_ENUM("OUT3 Phase Select", rtq9128_out3_phase_enum),
+ SOC_ENUM("OUT4 Phase Select", rtq9128_out4_phase_enum),
+ SOC_ENUM("DVDD UV Threshold Select", rtq9128_dvdduv_select_enum),
+};
+
+static int rtq9128_dac_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);
+ unsigned int shift, mask;
+ int ret;
+
+ dev_dbg(comp->dev, "%s: %s event %d\n", __func__, w->name, event);
+
+ if (snd_soc_dapm_widget_name_cmp(w, "DAC1") == 0)
+ shift = 6;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC2") == 0)
+ shift = 4;
+ else if (snd_soc_dapm_widget_name_cmp(w, "DAC3") == 0)
+ shift = 2;
+ else
+ shift = 0;
+
+ mask = RTQ9128_CHSTAT_VAL_MASK << shift;
+
+ /* Turn channel state to Normal or HiZ */
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_STATE_CTRL, mask,
+ event != SND_SOC_DAPM_POST_PMU);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * For each channel turns on, HW will trigger DC load detect and DC
+ * offset calibration, the time is needed for all the actions done.
+ */
+ if (event == SND_SOC_DAPM_POST_PMU)
+ msleep(25);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9128_dapm_widgets[] = {
+ SND_SOC_DAPM_DAC_E("DAC1", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC2", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC3", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DAC4", NULL, SND_SOC_NOPM, 0, 0, rtq9128_dac_power_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT1"),
+ SND_SOC_DAPM_OUTPUT("OUT2"),
+ SND_SOC_DAPM_OUTPUT("OUT3"),
+ SND_SOC_DAPM_OUTPUT("OUT4"),
+};
+
+static const struct snd_soc_dapm_route rtq9128_dapm_routes[] = {
+ { "DAC1", NULL, "Playback" },
+ { "DAC2", NULL, "Playback" },
+ { "DAC3", NULL, "Playback" },
+ { "DAC4", NULL, "Playback" },
+ { "OUT1", NULL, "DAC1" },
+ { "OUT2", NULL, "DAC2" },
+ { "OUT3", NULL, "DAC3" },
+ { "OUT4", NULL, "DAC4" },
+ { "Capture", NULL, "DAC1" },
+ { "Capture", NULL, "DAC2" },
+ { "Capture", NULL, "DAC3" },
+ { "Capture", NULL, "DAC4" },
+};
+
+static const struct rtq9128_init_reg rtq9128_tka470b_tables[] = {
+ { 0xA0, 0xEF },
+ { 0x0D, 0x00 },
+ { 0x03, 0x05 },
+ { 0x05, 0x31 },
+ { 0x06, 0x23 },
+ { 0x70, 0x11 },
+ { 0x75, 0x1F },
+ { 0xB6, 0x03 },
+ { 0xB9, 0x03 },
+ { 0xB8, 0x03 },
+ { 0xC1, 0xFF },
+ { 0xF8, 0x72 },
+ { 0x30, 0x180 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dh_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0xB2, 0xFF },
+ { 0xB3, 0xFF },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static const struct rtq9128_init_reg rtq9128_dl_tables[] = {
+ { 0x0F, 0x00 },
+ { 0x03, 0x0D },
+ { 0x30, 0x180 },
+ { 0x8A, 0x55 },
+ { 0x72, 0x00 },
+ { 0xB1, 0xE3 },
+};
+
+static int rtq9128_component_probe(struct snd_soc_component *comp)
+{
+ const struct rtq9128_init_reg *table, *curr;
+ size_t table_size;
+ unsigned int val;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(comp->dev);
+ if (ret < 0) {
+ dev_err(comp->dev, "Failed to resume device (%d)\n", ret);
+ return ret;
+ }
+
+ val = snd_soc_component_read(comp, RTQ9128_REG_EFUSE_DATA);
+
+ switch (FIELD_GET(RTQ9128_DIE_CHECK_MASK, val)) {
+ case RTQ9128_TKA470B_VAL:
+ table = rtq9128_tka470b_tables;
+ table_size = ARRAY_SIZE(rtq9128_tka470b_tables);
+ break;
+ case RTQ9128_RTQ9128DH_VAL:
+ table = rtq9128_dh_tables;
+ table_size = ARRAY_SIZE(rtq9128_dh_tables);
+ break;
+ default:
+ table = rtq9128_dl_tables;
+ table_size = ARRAY_SIZE(rtq9128_dl_tables);
+ break;
+ }
+
+ for (i = 0, curr = table; i < table_size; i++, curr++) {
+ ret = snd_soc_component_write(comp, curr->reg, curr->val);
+ if (ret < 0)
+ return ret;
+ }
+
+ pm_runtime_mark_last_busy(comp->dev);
+ pm_runtime_put(comp->dev);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9128_comp_driver = {
+ .probe = rtq9128_component_probe,
+ .controls = rtq9128_snd_ctrls,
+ .num_controls = ARRAY_SIZE(rtq9128_snd_ctrls),
+ .dapm_widgets = rtq9128_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9128_dapm_widgets),
+ .dapm_routes = rtq9128_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9128_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9128_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct device *dev = dai->dev;
+
+ dev_dbg(dev, "%s: fmt 0x%8x\n", __func__, fmt);
+
+ /* Only support bitclock & framesync as consumer */
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
+ dev_err(dev, "Only support BCK and LRCK as consumer\n");
+ return -EINVAL;
+ }
+
+ /* Store here and will be used in runtime hw_params for DAI format setting */
+ data->daifmt = fmt;
+
+ return 0;
+}
+
+static int rtq9128_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int mask, start_loc, srcin_select;
+ int i, frame_length, ret;
+
+ dev_dbg(dev, "%s: slot %d slot_width %d, tx/rx mask 0x%x 0x%x\n", __func__, slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slot_width % 8) {
+ dev_err(dev, "Invalid slot numbers (%d) or width (%d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ /* HW supported maximum frame length 512 */
+ frame_length = slots * slot_width;
+ if (frame_length > 512) {
+ dev_err(dev, "frame length exceed the maximum (%d)\n", frame_length);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(tx_mask) > slots || hweight_long(rx_mask) > slots ||
+ fls(tx_mask) > slots || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid tx/rx mask (0x%x/0x%x)\n", tx_mask, rx_mask);
+ return -EINVAL;
+ }
+
+ for (mask = tx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_TX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign tx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ for (mask = rx_mask, i = 0; i < 4 && mask; i++) {
+ start_loc = (ffs(mask) - 1) * slot_width / 8;
+ mask &= ~BIT(ffs(mask) - 1);
+
+ ret = snd_soc_component_write(comp, RTQ9128_REG_TDM_RX_CH1 + i, start_loc);
+ if (ret < 0) {
+ dev_err(dev, "Failed to assign rx_loc %d (%d)\n", i, ret);
+ return ret;
+ }
+ }
+
+ srcin_select = data->tdm_input_data2_select ? RTQ9128_TDMSRCIN_MASK : 0;
+ ret = snd_soc_component_update_bits(comp, RTQ9128_REG_SDO_SEL, RTQ9128_TDMSRCIN_MASK,
+ srcin_select);
+ if (ret < 0) {
+ dev_err(dev, "Failed to configure TDM source input select\n");
+ return ret;
+ }
+
+ data->tdm_slots = slots;
+ data->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9128_dai_hw_params(struct snd_pcm_substream *stream, struct snd_pcm_hw_params *param,
+ struct snd_soc_dai *dai)
+{
+ struct rtq9128_data *data = snd_soc_dai_get_drvdata(dai);
+ unsigned int width, slot_width, bitrate, audbit, dolen;
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int fmtval, audfmt;
+ int ret;
+
+ dev_dbg(dev, "%s: width %d\n", __func__, params_width(param));
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, data->daifmt);
+ if (data->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM is used, format only support DSP_A or DSP_B\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 8;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 9;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 10;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = data->tdm_slots ? 12 : 11;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = data->tdm_slots ? 4 : 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported format 0x%8x\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 18:
+ audbit = 1;
+ break;
+ case 20:
+ audbit = 2;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width (%d)\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+
+ if (data->tdm_slots) {
+ if (slot_width > data->tdm_slot_width) {
+ dev_err(dev, "slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ /* Check BCK not exceed the maximum supported rate 24.576MHz */
+ bitrate = data->tdm_slots * data->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "bitrate exceed the maximum (%d)\n", bitrate);
+ return -EINVAL;
+ }
+
+ /* If TDM is used, configure slot width as TDM slot witdh */
+ slot_width = data->tdm_slot_width;
+ }
+
+ switch (slot_width) {
+ case 16:
+ dolen = 0;
+ break;
+ case 24:
+ dolen = 1;
+ break;
+ case 32:
+ dolen = 2;
+ break;
+ default:
+ dev_err(dev, "Unsupported slot width (%d)\n", slot_width);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDFMT_MASK, audfmt);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_I2S_OPT, RTQ9128_AUDBIT_MASK, audbit);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_SDO_SEL, RTQ9128_DOLEN_MASK, dolen);
+ return ret < 0 ? ret : 0;
+}
+
+static int rtq9128_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ int ret;
+
+ dev_dbg(dev, "%s: mute (%d), stream (%d)\n", __func__, mute, stream);
+
+ ret = snd_soc_component_write_field(comp, RTQ9128_REG_DSP_EN, RTQ9128_MSMUTE_MASK,
+ mute ? 1 : 0);
+ return ret < 0 ? ret : 0;
+}
+
+static const struct snd_soc_dai_ops rtq9128_dai_ops = {
+ .set_fmt = rtq9128_dai_set_fmt,
+ .set_tdm_slot = rtq9128_dai_set_tdm_slot,
+ .hw_params = rtq9128_dai_hw_params,
+ .mute_stream = rtq9128_dai_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define RTQ9128_FMTS_MASK (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver rtq9128_dai = {
+ .name = "rtq9128-aif",
+ .playback = {
+ .stream_name = "Playback",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = RTQ9128_FMTS_MASK,
+ .channels_min = 1,
+ .channels_max = 4,
+ },
+ .ops = &rtq9128_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static int rtq9128_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9128_data *data;
+ struct regmap *regmap;
+ unsigned int venid;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(data->enable))
+ return dev_err_probe(dev, PTR_ERR(data->enable), "Failed to get 'enable' gpio\n");
+ else if (data->enable)
+ usleep_range(10000, 11000);
+
+ data->tdm_input_data2_select = device_property_read_bool(dev,
+ "richtek,tdm-input-data2-select");
+
+ i2c_set_clientdata(i2c, data);
+
+ /*
+ * Due to the bad design to combine SOFT_RESET bit with other function,
+ * directly use generic i2c API to trigger SOFT_RESET.
+ */
+ ret = i2c_smbus_write_byte_data(i2c, RTQ9128_REG_MISC, RTQ9128_SOFT_RESET_VAL);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger software reset\n");
+
+ /* After trigger soft reset, have to wait 10ms for digital reset done */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, &rtq9128_regmap_bus, dev, &rtq9128_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_read(regmap, RTQ9128_REG_VENDOR_ID, &venid);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get vendor id\n");
+
+ venid = FIELD_GET(RTQ9128_VENDOR_ID_MASK, venid);
+ if (venid != RTQ9128_VENDOR_ID_VAL)
+ return dev_err_probe(dev, -ENODEV, "Vendor ID not match (0x%x)\n", venid);
+
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9128_comp_driver, &rtq9128_dai, 1);
+}
+
+static int rtq9128_pm_runtime_suspend(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to ultra low quiescent */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_ULQM_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 0);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+
+ return 0;
+}
+
+static int rtq9128_pm_runtime_resume(struct device *dev)
+{
+ struct rtq9128_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ /* If 'enable' gpio not specified, change all channels to default Hi-Z */
+ if (!data->enable)
+ return regmap_write(regmap, RTQ9128_REG_STATE_CTRL, RTQ9128_ALLCH_HIZ_VAL);
+
+ gpiod_set_value_cansleep(data->enable, 1);
+
+ /* Wait digital block to be ready */
+ usleep_range(10000, 11000);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
+static const struct dev_pm_ops rtq9128_pm_ops = {
+ RUNTIME_PM_OPS(rtq9128_pm_runtime_suspend, rtq9128_pm_runtime_resume, NULL)
+};
+
+static const struct of_device_id rtq9128_device_table[] = {
+ { .compatible = "richtek,rtq9128" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9128_device_table);
+
+static struct i2c_driver rtq9128_driver = {
+ .driver = {
+ .name = "rtq9128",
+ .of_match_table = rtq9128_device_table,
+ .pm = pm_ptr(&rtq9128_pm_ops),
+ },
+ .probe = rtq9128_probe,
+};
+module_i2c_driver(rtq9128_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("RTQ9128 4CH 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 000000000000..574c08b14f0c
--- /dev/null
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -0,0 +1,275 @@
+// 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 <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+
+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",
+ },
+ .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 <pierre-louis.bossart@linux.intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index e182e6569bbd..2cc8efe3d896 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1,12 +1,8 @@
-/*
- * sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
- *
- * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * 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.
- */
+// SPDX-License-Identifier: GPL-2.0
+//
+// sgtl5000.c -- SGTL5000 ALSA SoC Audio driver
+//
+// Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -17,11 +13,11 @@
#include <linux/i2c.h>
#include <linux/clk.h>
#include <linux/log2.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/consumer.h>
-#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/tlv.h>
#include <sound/pcm.h>
@@ -35,10 +31,16 @@
#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_CLK_CTRL, 0x0008 },
{ SGTL5000_CHIP_I2S_CTRL, 0x0010 },
{ SGTL5000_CHIP_SSS_CTRL, 0x0010 },
{ SGTL5000_CHIP_ADCDAC_CTRL, 0x020c },
@@ -47,12 +49,10 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_CHIP_ANA_ADC_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_HP_CTRL, 0x1818 },
{ SGTL5000_CHIP_ANA_CTRL, 0x0111 },
- { SGTL5000_CHIP_LINREG_CTRL, 0x0000 },
{ SGTL5000_CHIP_REF_CTRL, 0x0000 },
{ SGTL5000_CHIP_MIC_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_CTRL, 0x0000 },
{ SGTL5000_CHIP_LINE_OUT_VOL, 0x0404 },
- { SGTL5000_CHIP_ANA_POWER, 0x7060 },
{ SGTL5000_CHIP_PLL_CTRL, 0x5000 },
{ SGTL5000_CHIP_CLK_TOP_CTRL, 0x0000 },
{ SGTL5000_CHIP_ANA_STATUS, 0x0000 },
@@ -71,12 +71,26 @@ static const struct reg_default sgtl5000_reg_defaults[] = {
{ SGTL5000_DAP_EQ_BASS_BAND4, 0x002f },
{ SGTL5000_DAP_MAIN_CHAN, 0x8000 },
{ SGTL5000_DAP_MIX_CHAN, 0x0000 },
- { SGTL5000_DAP_AVC_CTRL, 0x0510 },
+ { 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,
@@ -92,41 +106,35 @@ static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
"VDDD"
};
-#define LDO_CONSUMER_NAME "VDDD_LDO"
#define LDO_VOLTAGE 1200000
+#define LINREG_VDDD ((1600 - LDO_VOLTAGE / 1000) / 50)
-static struct regulator_consumer_supply ldo_consumer[] = {
- REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
+enum sgtl5000_micbias_resistor {
+ SGTL5000_MICBIAS_OFF = 0,
+ SGTL5000_MICBIAS_2K = 2,
+ SGTL5000_MICBIAS_4K = 4,
+ SGTL5000_MICBIAS_8K = 8,
};
-static struct regulator_init_data ldo_init_data = {
- .constraints = {
- .min_uV = 1200000,
- .max_uV = 1200000,
- .valid_modes_mask = REGULATOR_MODE_NORMAL,
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &ldo_consumer[0],
+enum {
+ I2S_LRCLK_STRENGTH_DISABLE,
+ I2S_LRCLK_STRENGTH_LOW,
+ I2S_LRCLK_STRENGTH_MEDIUM,
+ I2S_LRCLK_STRENGTH_HIGH,
};
-/*
- * sgtl5000 internal ldo regulator,
- * enabled when VDDD not provided
- */
-struct ldo_regulator {
- struct regulator_desc desc;
- struct regulator_dev *dev;
- int voltage;
- void *codec_data;
- bool enabled;
+enum {
+ I2S_SCLK_STRENGTH_DISABLE,
+ I2S_SCLK_STRENGTH_LOW,
+ I2S_SCLK_STRENGTH_MEDIUM,
+ I2S_SCLK_STRENGTH_HIGH,
};
-enum sgtl5000_micbias_resistor {
- SGTL5000_MICBIAS_OFF = 0,
- SGTL5000_MICBIAS_2K = 2,
- SGTL5000_MICBIAS_4K = 4,
- SGTL5000_MICBIAS_8K = 8,
+enum {
+ HP_POWER_EVENT,
+ DAC_POWER_EVENT,
+ ADC_POWER_EVENT,
+ LAST_POWER_EVENT = ADC_POWER_EVENT
};
/* sgtl5000 private structure in codec */
@@ -135,14 +143,117 @@ struct sgtl5000_priv {
int master; /* i2s master or not */
int fmt; /* i2s data format */
struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
- struct ldo_regulator *ldo;
+ 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
@@ -155,54 +266,65 @@ struct sgtl5000_priv {
static int mic_bias_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ 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_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ 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_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK, 0);
break;
}
return 0;
}
-/*
- * As manual described, ADC/DAC only works when VAG powerup,
- * So enabled VAG before ADC/DAC up.
- * In power down case, we need wait 400ms when vag fully ramped down.
- */
-static int power_vag_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int vag_and_mute_control(struct snd_soc_component *component,
+ int event, int event_source)
{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- const u32 mask = SGTL5000_DAC_POWERUP | SGTL5000_ADC_POWERUP;
+ 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:
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+ 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:
- /*
- * Don't clear VAG_POWERUP, when both DAC and ADC are
- * operational to prevent inadvertently starving the
- * other one of them.
- */
- if ((snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER) &
- mask) != mask) {
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VAG_POWERUP, 0);
- msleep(400);
- }
+ 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;
@@ -211,6 +333,41 @@ static int power_vag_event(struct snd_soc_dapm_widget *w,
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"
@@ -223,17 +380,54 @@ static SOC_ENUM_SINGLE_DECL(adc_enum,
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[] = {
- "DAC", "LINE_IN"
+ "ADC", "I2S", "Rsvrd", "DAP"
};
static SOC_ENUM_SINGLE_DECL(dac_enum,
- SGTL5000_CHIP_ANA_CTRL, 6,
+ SGTL5000_CHIP_SSS_CTRL, SGTL5000_DAC_SEL_SHIFT,
dac_mux_text);
static const struct snd_kcontrol_new dac_mux =
-SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+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"),
@@ -246,11 +440,19 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
mic_bias_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
- SND_SOC_DAPM_PGA("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0),
+ 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, &dac_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",
@@ -262,11 +464,12 @@ static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
0, SGTL5000_CHIP_DIG_POWER,
1, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
- SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
-
- SND_SOC_DAPM_PRE("VAG_POWER_PRE", power_vag_event),
- SND_SOC_DAPM_POST("VAG_POWER_POST", power_vag_event),
+ 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 */
@@ -277,7 +480,19 @@ static const struct snd_soc_dapm_route sgtl5000_dapm_routes[] = {
{"ADC", NULL, "Capture Mux"}, /* adc_mux --> adc */
{"AIFOUT", NULL, "ADC"}, /* adc --> i2s_out */
- {"DAC", NULL, "AIFIN"}, /* i2s-->dac,skip audio mux */
+ {"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 */
@@ -325,12 +540,12 @@ static int dac_info_volsw(struct snd_kcontrol *kcontrol,
static int dac_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
- reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+ 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;
@@ -378,7 +593,7 @@ static int dac_get_volsw(struct snd_kcontrol *kcontrol,
static int dac_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
int reg;
int l;
int r;
@@ -398,7 +613,66 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+ 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;
}
@@ -406,15 +680,29 @@ static int dac_put_volsw(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
/* tlv for mic gain, 0db 20db 30db 40db */
-static const unsigned int mic_gain_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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 */
{
@@ -432,27 +720,73 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
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_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+static int sgtl5000_mute_stream(struct snd_soc_dai *codec_dai, int mute, int direction)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+ struct snd_soc_component *component = codec_dai->component;
+ u16 i2s_pwr = SGTL5000_I2S_IN_POWERUP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
- adcdac_ctrl, mute ? adcdac_ctrl : 0);
+ /*
+ * 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;
}
@@ -460,8 +794,8 @@ static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
/* set codec format */
static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
u16 i2sctl = 0;
sgtl5000->master = 0;
@@ -472,9 +806,9 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
* - clock and frame master
*/
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
- case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBP_CFP:
i2sctl |= SGTL5000_I2S_MASTER;
sgtl5000->master = 1;
break;
@@ -519,7 +853,7 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_I2S_CTRL, i2sctl);
return 0;
}
@@ -528,8 +862,8 @@ static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
switch (clk_id) {
case SGTL5000_SYSCLK:
@@ -555,9 +889,9 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
* and above.
* 3. usage of sys_mclk is preferred over pll to save power.
*/
-static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+static int sgtl5000_set_clock(struct snd_soc_component *component, int frame_rate)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ struct sgtl5000_priv *sgtl5000 = snd_soc_component_get_drvdata(component);
int clk_ctl = 0;
int sys_fs; /* sample freq */
@@ -610,7 +944,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
break;
default:
- dev_err(codec->dev, "frame rate %d not supported\n",
+ dev_err(component->dev, "frame rate %d not supported\n",
frame_rate);
return -EINVAL;
}
@@ -639,9 +973,9 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
SGTL5000_MCLK_FREQ_SHIFT;
} else {
- dev_err(codec->dev,
+ dev_err(component->dev,
"PLL not supported in slave mode\n");
- dev_err(codec->dev, "%d ratio is not supported. "
+ 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;
@@ -674,31 +1008,31 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
- snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
if (div2)
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
SGTL5000_INPUT_FREQ_DIV2);
else
- snd_soc_update_bits(codec,
+ snd_soc_component_update_bits(component,
SGTL5000_CHIP_CLK_TOP_CTRL,
SGTL5000_INPUT_FREQ_DIV2,
0);
/* power up pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ 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_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
} else {
/* otherwise, clk_ctrl must be set before pll power down */
- snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+ snd_soc_component_write(component, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
/* power down pll */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
0);
}
@@ -714,8 +1048,8 @@ 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_codec *codec = dai->codec;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -723,7 +1057,7 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
/* sysclk should already set */
if (!sgtl5000->sysclk) {
- dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+ dev_err(component->dev, "%s: set sysclk first!\n", __func__);
return -EFAULT;
}
@@ -733,11 +1067,11 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
stereo = SGTL5000_ADC_STEREO;
/* set mono to save power */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+ 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(codec, params_rate(params));
+ ret = sgtl5000_set_clock(component, params_rate(params));
if (ret)
return ret;
@@ -771,162 +1105,13 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_I2S_CTRL,
SGTL5000_I2S_DLEN_MASK | SGTL5000_I2S_SCLKFREQ_MASK,
i2s_ctl);
return 0;
}
-#ifdef CONFIG_REGULATOR
-static int ldo_regulator_is_enabled(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->enabled;
-}
-
-static int ldo_regulator_enable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
- int reg;
-
- if (ldo_regulator_is_enabled(dev))
- return 0;
-
- /* set regulator value firstly */
- reg = (1600 - ldo->voltage / 1000) / 50;
- reg = clamp(reg, 0x0, 0xf);
-
- /* amend the voltage value, unit: uV */
- ldo->voltage = (1600 - reg * 50) * 1000;
-
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, reg);
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- SGTL5000_LINEREG_D_POWERUP);
-
- /* when internal ldo is enabled, simple digital power can be disabled */
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
-
- ldo->enabled = 1;
- return 0;
-}
-
-static int ldo_regulator_disable(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
- struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
-
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINEREG_D_POWERUP,
- 0);
-
- /* clear voltage info */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0);
-
- ldo->enabled = 0;
-
- return 0;
-}
-
-static int ldo_regulator_get_voltage(struct regulator_dev *dev)
-{
- struct ldo_regulator *ldo = rdev_get_drvdata(dev);
-
- return ldo->voltage;
-}
-
-static struct regulator_ops ldo_regulator_ops = {
- .is_enabled = ldo_regulator_is_enabled,
- .enable = ldo_regulator_enable,
- .disable = ldo_regulator_disable,
- .get_voltage = ldo_regulator_get_voltage,
-};
-
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- struct ldo_regulator *ldo;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct regulator_config config = { };
-
- ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
-
- if (!ldo)
- return -ENOMEM;
-
- ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
- if (!ldo->desc.name) {
- kfree(ldo);
- dev_err(codec->dev, "failed to allocate decs name memory\n");
- return -ENOMEM;
- }
-
- ldo->desc.type = REGULATOR_VOLTAGE;
- ldo->desc.owner = THIS_MODULE;
- ldo->desc.ops = &ldo_regulator_ops;
- ldo->desc.n_voltages = 1;
-
- ldo->codec_data = codec;
- ldo->voltage = voltage;
-
- config.dev = codec->dev;
- config.driver_data = ldo;
- config.init_data = init_data;
-
- ldo->dev = regulator_register(&ldo->desc, &config);
- if (IS_ERR(ldo->dev)) {
- int ret = PTR_ERR(ldo->dev);
-
- dev_err(codec->dev, "failed to register regulator\n");
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return ret;
- }
- sgtl5000->ldo = ldo;
-
- return 0;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- struct ldo_regulator *ldo = sgtl5000->ldo;
-
- if (!ldo)
- return 0;
-
- regulator_unregister(ldo->dev);
- kfree(ldo->desc.name);
- kfree(ldo);
-
- return 0;
-}
-#else
-static int ldo_regulator_register(struct snd_soc_codec *codec,
- struct regulator_init_data *init_data,
- int voltage)
-{
- dev_err(codec->dev, "this setup needs regulator support in the kernel\n");
- return -EINVAL;
-}
-
-static int ldo_regulator_remove(struct snd_soc_codec *codec)
-{
- return 0;
-}
-#endif
-
/*
* set dac bias
* common state changes:
@@ -937,49 +1122,34 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
* stop:
* on --> prepare --> standby
*/
-static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+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;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- ret = regulator_bulk_enable(
- ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- return ret;
- udelay(10);
-
- regcache_cache_only(sgtl5000->regmap, false);
-
- ret = regcache_sync(sgtl5000->regmap);
- if (ret != 0) {
- dev_err(codec->dev,
- "Failed to restore cache: %d\n", ret);
-
- regcache_cache_only(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-
- return ret;
- }
+ 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(sgtl5000->regmap, true);
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
+ regcache_cache_only(sgtl->regmap, true);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP, 0);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -990,9 +1160,10 @@ static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops sgtl5000_ops = {
.hw_params = sgtl5000_pcm_hw_params,
- .digital_mute = sgtl5000_digital_mute,
+ .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 = {
@@ -1016,7 +1187,7 @@ static struct snd_soc_dai_driver sgtl5000_dai = {
.formats = SGTL5000_FORMATS,
},
.ops = &sgtl5000_ops,
- .symmetric_rates = 1,
+ .symmetric_rate = 1,
};
static bool sgtl5000_volatile(struct device *dev, unsigned int reg)
@@ -1092,6 +1263,19 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
}
/*
+ * 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
@@ -1101,9 +1285,8 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
* and should be set according to:
* 1. vddd provided by external or not
* 2. vdda and vddio voltage value. > 3.1v or not
- * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
*/
-static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+static int sgtl5000_set_power_regs(struct snd_soc_component *component)
{
int vddd;
int vdda;
@@ -1111,25 +1294,31 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
u16 ana_pwr;
u16 lreg_ctrl;
int vag;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+ 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 = regulator_get_voltage(sgtl5000->supplies[VDDD].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(codec->dev, "regulator voltage not set correctly\n");
+ 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(codec->dev,
+ dev_err(component->dev,
"exceed max voltage vdda %dmV vddio %dmV vddd %dmV\n",
vdda, vddio, vddd);
@@ -1137,55 +1326,36 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
}
/* reset value */
- ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+ 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_read(codec, SGTL5000_CHIP_LINREG_CTRL);
+ 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_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL,
+ 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 if (vddio >= 3100 && vdda >= 3100) {
+ } else {
+ ana_pwr &= ~SGTL5000_VDDC_CHRGPMP_POWERUP;
/*
- * if vddio and vddd > 3.1v,
- * charge pump should be clean before set ana_pwr
+ * if vddio == vdda the source of charge pump should be
+ * assigned manually to VDDIO
*/
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_VDDC_CHRGPMP_POWERUP, 0);
-
- /* VDDC use VDDIO rail */
- lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
- lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
- SGTL5000_VDDC_MAN_ASSN_SHIFT;
+ 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_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
-
- snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
-
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0x8);
+ snd_soc_component_write(component, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
- /*
- * if vddd linear reg has been enabled,
- * simple digital supply should be clear to get
- * proper VDDD voltage.
- */
- if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
- else
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP |
- SGTL5000_STARTUP_POWERUP,
- 0);
+ snd_soc_component_write(component, SGTL5000_CHIP_ANA_POWER, ana_pwr);
/*
* set ADC/DAC VAG to vdda / 2,
@@ -1200,196 +1370,172 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
else
vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+ 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) */
- vag = vddio / 2;
- if (vag <= SGTL5000_LINE_OUT_GND_BASE)
- vag = 0;
- else if (vag >= SGTL5000_LINE_OUT_GND_BASE +
+ 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)
- vag = SGTL5000_LINE_OUT_GND_MAX;
+ lo_vag = SGTL5000_LINE_OUT_GND_MAX;
else
- vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+ lo_vag = (lo_vag - SGTL5000_LINE_OUT_GND_BASE) /
SGTL5000_LINE_OUT_GND_STP;
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_LINE_OUT_CTRL,
SGTL5000_LINE_OUT_CURRENT_MASK |
SGTL5000_LINE_OUT_GND_MASK,
- vag << SGTL5000_LINE_OUT_GND_SHIFT |
+ lo_vag << SGTL5000_LINE_OUT_GND_SHIFT |
SGTL5000_LINE_OUT_CURRENT_360u <<
SGTL5000_LINE_OUT_CURRENT_SHIFT);
- return 0;
-}
-
-static int sgtl5000_replace_vddd_with_ldo(struct snd_soc_codec *codec)
-{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- int ret;
-
- /* set internal ldo to 1.2v */
- ret = ldo_regulator_register(codec, &ldo_init_data, LDO_VOLTAGE);
- if (ret) {
- dev_err(codec->dev,
- "Failed to register vddd internal supplies: %d\n", ret);
- return ret;
+ /*
+ * 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;
}
- sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+ 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);
- dev_info(codec->dev, "Using internal LDO instead of VDDD\n");
return 0;
}
-static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+static int sgtl5000_enable_regulators(struct i2c_client *client)
{
int ret;
int i;
int external_vddd = 0;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
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];
- /* External VDDD only works before revision 0x11 */
- if (sgtl5000->revision < 0x11) {
- vddd = regulator_get_optional(codec->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);
- }
- }
-
- if (!external_vddd) {
- ret = sgtl5000_replace_vddd_with_ldo(codec);
- if (ret)
- return ret;
+ 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);
}
- ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->num_supplies = ARRAY_SIZE(sgtl5000->supplies)
+ - 1 + external_vddd;
+ ret = regulator_bulk_get(&client->dev, sgtl5000->num_supplies,
sgtl5000->supplies);
if (ret)
- goto err_ldo_remove;
-
- ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- if (ret)
- goto err_regulator_free;
-
- /* wait for all power rails bring up */
- udelay(10);
+ return ret;
- return 0;
+ ret = regulator_bulk_enable(sgtl5000->num_supplies,
+ sgtl5000->supplies);
+ if (!ret)
+ usleep_range(10, 20);
+ else
+ regulator_bulk_free(sgtl5000->num_supplies,
+ sgtl5000->supplies);
-err_regulator_free:
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
-err_ldo_remove:
- if (!external_vddd)
- ldo_regulator_remove(codec);
return ret;
-
}
-static int sgtl5000_probe(struct snd_soc_codec *codec)
+static int sgtl5000_probe(struct snd_soc_component *component)
{
int ret;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- ret = sgtl5000_enable_regulators(codec);
- if (ret)
- return 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(codec);
+ ret = sgtl5000_set_power_regs(component);
if (ret)
goto err;
/* enable small pop, introduce 400ms delay in turning off */
- snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP, 1);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_REF_CTRL,
+ SGTL5000_SMALL_POP, SGTL5000_SMALL_POP);
/* disable short cut detector */
- snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+ snd_soc_component_write(component, SGTL5000_CHIP_SHORT_CTRL, 0);
- /*
- * set i2s as default input of sound switch
- * TODO: add sound switch to control and dapm widge.
- */
- snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
- SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
- snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER,
+ snd_soc_component_write(component, SGTL5000_CHIP_DIG_POWER,
SGTL5000_ADC_EN | SGTL5000_DAC_EN);
/* enable dac volume ramp by default */
- snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+ snd_soc_component_write(component, SGTL5000_CHIP_ADCDAC_CTRL,
SGTL5000_DAC_VOL_RAMP_EN |
SGTL5000_DAC_MUTE_RIGHT |
SGTL5000_DAC_MUTE_LEFT);
- snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+ 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_write(codec, SGTL5000_CHIP_ANA_CTRL,
- SGTL5000_HP_ZCD_EN |
- SGTL5000_ADC_ZCD_EN);
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_ANA_CTRL,
+ zcd_mask, zcd_mask);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+ snd_soc_component_update_bits(component, SGTL5000_CHIP_MIC_CTRL,
SGTL5000_BIAS_R_MASK,
sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
- snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
- SGTL5000_BIAS_R_MASK,
- sgtl5000->micbias_voltage << 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);
/*
- * disable DAP
+ * enable DAP Graphic EQ
* TODO:
- * Enable DAP in kcontrol and dapm.
+ * Add control for changing between PEQ/Tone Control/GEQ
*/
- snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+ 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:
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return ret;
}
-static int sgtl5000_remove(struct snd_soc_codec *codec)
+static int sgtl5000_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
+ /* return dai id 0, whatever the endpoint index */
return 0;
}
-static struct snd_soc_codec_driver sgtl5000_driver = {
- .probe = sgtl5000_probe,
- .remove = sgtl5000_remove,
- .set_bias_level = sgtl5000_set_bias_level,
- .suspend_bias_off = true,
- .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),
+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 = {
@@ -1416,8 +1562,9 @@ static const struct regmap_config sgtl5000_regmap = {
* 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 int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
+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++) {
@@ -1425,52 +1572,64 @@ static int sgtl5000_fill_defaults(struct sgtl5000_priv *sgtl5000)
index = sgtl5000_reg_defaults[i].reg;
ret = regmap_write(sgtl5000->regmap, index, val);
if (ret)
- return ret;
+ dev_err(&client->dev,
+ "%s: error %d setting reg 0x%02x to 0x%04x\n",
+ __func__, ret, index, val);
}
-
- return 0;
}
-static int sgtl5000_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+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);
- return ret;
+ goto disable_regs;
}
sgtl5000->mclk = devm_clk_get(&client->dev, NULL);
if (IS_ERR(sgtl5000->mclk)) {
ret = PTR_ERR(sgtl5000->mclk);
- dev_err(&client->dev, "Failed to get mclock: %d\n", ret);
/* Defer the probe to see if the clk will be provided later */
if (ret == -ENOENT)
- return -EPROBE_DEFER;
- return ret;
+ 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)
- return ret;
+ 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, &reg);
- if (ret)
+ 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) {
@@ -1484,6 +1643,78 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
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)) {
@@ -1518,21 +1749,31 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
else {
sgtl5000->micbias_voltage = 0;
dev_err(&client->dev,
- "Unsuitable MicBias resistor\n");
+ "Unsuitable MicBias voltage\n");
}
} else {
sgtl5000->micbias_voltage = 0;
}
}
- i2c_set_clientdata(client, sgtl5000);
+ 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 */
- ret = sgtl5000_fill_defaults(sgtl5000);
- if (ret)
- goto disable_clk;
+ sgtl5000_fill_defaults(client);
- ret = snd_soc_register_codec(&client->dev,
+ ret = devm_snd_soc_register_component(&client->dev,
&sgtl5000_driver, &sgtl5000_dai, 1);
if (ret)
goto disable_clk;
@@ -1541,20 +1782,34 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
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 int sgtl5000_i2c_remove(struct i2c_client *client)
+static void sgtl5000_i2c_remove(struct i2c_client *client)
{
struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
- snd_soc_unregister_codec(&client->dev);
+ 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);
- return 0;
+ 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},
+ {"sgtl5000"},
{},
};
@@ -1568,12 +1823,12 @@ MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
static struct i2c_driver sgtl5000_i2c_driver = {
.driver = {
- .name = "sgtl5000",
- .owner = THIS_MODULE,
- .of_match_table = sgtl5000_dt_ids,
- },
+ .name = "sgtl5000",
+ .of_match_table = sgtl5000_dt_ids,
+ },
.probe = sgtl5000_i2c_probe,
.remove = sgtl5000_i2c_remove,
+ .shutdown = sgtl5000_i2c_shutdown,
.id_table = sgtl5000_id,
};
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index bd7a344bf8c5..3a808c762299 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* sgtl5000.h - SGTL5000 audio codec interface
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
- *
- * 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 _SGTL5000_H
@@ -83,6 +80,7 @@
/*
* 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
@@ -92,6 +90,7 @@
/*
* 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
@@ -235,6 +234,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
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0
+#define SGTL5000_SMALL_POP 0x0001
/*
* SGTL5000_CHIP_MIC_CTRL
@@ -325,6 +325,7 @@
/*
* 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
@@ -397,4 +398,11 @@
#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
index 3e72964280c6..d87141ba8438 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sound/soc/codecs/si476x.c -- Codec driver for SI476X chips
*
@@ -5,16 +6,6 @@
* Copyright (C) 2013 Andrey Smirnov
*
* Author: Andrey Smirnov <andrew.smirnov@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
*/
#include <linux/module.h>
@@ -78,7 +69,7 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
int err;
u16 format = 0;
- if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC)
return -EINVAL;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -140,14 +131,14 @@ static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
si476x_core_lock(core);
- err = snd_soc_update_bits(codec_dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ 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->codec->dev, "Failed to set output format\n");
+ dev_err(codec_dai->component->dev, "Failed to set output format\n");
return err;
}
@@ -163,7 +154,7 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
rate = params_rate(params);
if (rate < 32000 || rate > 48000) {
- dev_err(dai->codec->dev, "Rate: %d is not supported\n", rate);
+ dev_err(dai->component->dev, "Rate: %d is not supported\n", rate);
return -EINVAL;
}
@@ -186,19 +177,19 @@ static int si476x_codec_hw_params(struct snd_pcm_substream *substream,
si476x_core_lock(core);
- err = snd_soc_write(dai->codec, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
+ err = snd_soc_component_write(dai->component, SI476X_DIGITAL_IO_OUTPUT_SAMPLE_RATE,
rate);
if (err < 0) {
- dev_err(dai->codec->dev, "Failed to set sample rate\n");
+ dev_err(dai->component->dev, "Failed to set sample rate\n");
goto out;
}
- err = snd_soc_update_bits(dai->codec, SI476X_DIGITAL_IO_OUTPUT_FORMAT,
+ 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->codec->dev, "Failed to set output width\n");
+ dev_err(dai->component->dev, "Failed to set output width\n");
goto out;
}
@@ -208,7 +199,7 @@ out:
return err;
}
-static struct snd_soc_dai_ops si476x_dai_ops = {
+static const struct snd_soc_dai_ops si476x_dai_ops = {
.hw_params = si476x_codec_hw_params,
.set_fmt = si476x_codec_set_dai_fmt,
};
@@ -231,31 +222,32 @@ static struct snd_soc_dai_driver si476x_dai = {
.ops = &si476x_dai_ops,
};
-static struct regmap *si476x_get_regmap(struct device *dev)
+static int si476x_probe(struct snd_soc_component *component)
{
- return dev_get_regmap(dev->parent, NULL);
+ snd_soc_component_init_regmap(component,
+ dev_get_regmap(component->dev->parent, NULL));
+
+ return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_si476x = {
- .get_regmap = si476x_get_regmap,
- .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),
+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 snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si476x,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_component_dev_si476x,
&si476x_dai, 1);
}
-static int si476x_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
MODULE_ALIAS("platform:si476x-codec");
static struct platform_driver si476x_platform_driver = {
@@ -263,7 +255,6 @@ static struct platform_driver si476x_platform_driver = {
.name = "si476x-codec",
},
.probe = si476x_platform_probe,
- .remove = si476x_platform_remove,
};
module_platform_driver(si476x_platform_driver);
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
index 21ca3a5e9f66..07c9d89ab24a 100644
--- a/sound/soc/codecs/sigmadsp-i2c.c
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -1,16 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include "sigmadsp.h"
@@ -31,7 +30,10 @@ static int sigmadsp_write_i2c(void *control_data,
kfree(buf);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int sigmadsp_read_i2c(void *control_data,
diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c
index 912861be5b87..ba9a6795e470 100644
--- a/sound/soc/codecs/sigmadsp-regmap.c
+++ b/sound/soc/codecs/sigmadsp-regmap.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/regmap.h>
@@ -27,7 +26,7 @@ static int sigmadsp_read_regmap(void *control_data,
}
/**
- * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * 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
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index d53680ac78e4..201f74e3a7ae 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Load Analog Devices SigmaStudio firmware files
*
* Copyright 2009-2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/crc32.h>
@@ -25,6 +24,8 @@
#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;
@@ -32,6 +33,7 @@ struct sigmadsp_control {
unsigned int num_bytes;
const char *name;
struct snd_kcontrol *kcontrol;
+ bool is_readback;
bool cached;
uint8_t cache[];
};
@@ -41,7 +43,7 @@ struct sigmadsp_data {
uint32_t samplerates;
unsigned int addr;
unsigned int length;
- uint8_t data[];
+ uint8_t data[] __counted_by(length);
};
struct sigma_fw_chunk {
@@ -117,8 +119,7 @@ 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 > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
- sigmadsp->ops->safeload)
+ if (ctrl->num_bytes <= 20 && sigmadsp->ops && sigmadsp->ops->safeload)
return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
ctrl->num_bytes);
else
@@ -143,7 +144,8 @@ static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
if (ret == 0) {
memcpy(ctrl->cache, data, ctrl->num_bytes);
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
}
mutex_unlock(&sigmadsp->lock);
@@ -166,7 +168,8 @@ static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
}
if (ret == 0) {
- ctrl->cached = true;
+ if (!ctrl->is_readback)
+ ctrl->cached = true;
memcpy(ucontrol->value.bytes.data, ctrl->cache,
ctrl->num_bytes);
}
@@ -224,15 +227,22 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
if (!ctrl)
return -ENOMEM;
- name = kzalloc(name_len + 1, GFP_KERNEL);
+ name = kmemdup_nul(ctrl_chunk->name, name_len, GFP_KERNEL);
if (!name) {
ret = -ENOMEM;
goto err_free_ctrl;
}
- memcpy(name, ctrl_chunk->name, name_len);
- name[name_len] = '\0';
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);
@@ -260,7 +270,7 @@ static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
length -= sizeof(*data_chunk);
- data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, length), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -403,7 +413,8 @@ static int process_sigma_action(struct sigmadsp *sigmadsp,
if (len < 3)
return -EINVAL;
- data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+ data = kzalloc(struct_size(data, data, size_sub(len, 2)),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -659,36 +670,19 @@ 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;
- struct snd_kcontrol_volatile *vd;
- struct snd_ctl_elem_id id;
bool active;
- bool changed = false;
+ int changed;
active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
-
- down_write(&card->controls_rwsem);
- if (!ctrl->kcontrol) {
- up_write(&card->controls_rwsem);
+ if (!ctrl->kcontrol)
return;
- }
-
- id = ctrl->kcontrol->id;
- vd = &ctrl->kcontrol->vd[0];
- if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
- vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
- changed = true;
- }
- up_write(&card->controls_rwsem);
-
- if (active && changed) {
+ 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);
}
-
- if (changed)
- snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
}
/**
@@ -811,4 +805,5 @@ int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
}
EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
+MODULE_DESCRIPTION("Analog Devices SigmaStudio firmware helpers");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index 614475cbb823..2783eff633a1 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Load firmware files from Analog Devices SigmaStudio
*
* Copyright 2009-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#ifndef __SIGMA_FIRMWARE_H__
@@ -45,7 +44,6 @@ struct sigmadsp {
struct sigmadsp *devm_sigmadsp_init(struct device *dev,
const struct sigmadsp_ops *ops, const char *firmware_name);
-void sigmadsp_reset(struct sigmadsp *sigmadsp);
int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
struct snd_pcm_substream *substream);
@@ -60,7 +58,7 @@ struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
int sigmadsp_attach(struct sigmadsp *sigmadsp,
struct snd_soc_component *component);
-int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+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 000000000000..d306c585b52b
--- /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 <jbrunet@baylibre.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#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 <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/simple-mux.c b/sound/soc/codecs/simple-mux.c
new file mode 100644
index 000000000000..390696440155
--- /dev/null
+++ b/sound/soc/codecs/simple-mux.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020 Bootlin SA
+ * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mux/driver.h>
+#include <linux/regulator/consumer.h>
+#include <sound/soc.h>
+
+#define MUX_TEXT_SIZE 2
+#define MUX_WIDGET_SIZE 4
+#define MUX_ROUTE_SIZE 3
+struct simple_mux {
+ struct gpio_desc *gpiod_mux;
+ unsigned int mux;
+ const char *mux_texts[MUX_TEXT_SIZE];
+ unsigned int idle_state;
+ struct soc_enum mux_enum;
+ struct snd_kcontrol_new mux_mux;
+ struct snd_soc_dapm_widget mux_widgets[MUX_WIDGET_SIZE];
+ struct snd_soc_dapm_route mux_routes[MUX_ROUTE_SIZE];
+ struct snd_soc_component_driver mux_driver;
+};
+
+static const char * const simple_mux_texts[MUX_TEXT_SIZE] = {
+ "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];
+
+ if (priv->idle_state != MUX_IDLE_AS_IS && dapm->bias_level < SND_SOC_BIAS_PREPARE)
+ return 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 int simple_mux_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 simple_mux *priv = snd_soc_component_get_drvdata(c);
+
+ if (priv->idle_state != MUX_IDLE_AS_IS) {
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->mux);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ gpiod_set_value_cansleep(priv->gpiod_mux, priv->idle_state);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget simple_mux_dapm_widgets[MUX_WIDGET_SIZE] = {
+ SND_SOC_DAPM_INPUT("IN1"),
+ SND_SOC_DAPM_INPUT("IN2"),
+ SND_SOC_DAPM_MUX_E("MUX", SND_SOC_NOPM, 0, 0, &simple_mux_mux, // see simple_mux_probe()
+ simple_mux_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route simple_mux_dapm_routes[MUX_ROUTE_SIZE] = {
+ { "OUT", NULL, "MUX" },
+ { "MUX", "Input 1", "IN1" }, // see simple_mux_probe()
+ { "MUX", "Input 2", "IN2" }, // see simple_mux_probe()
+};
+
+static int simple_mux_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct simple_mux *priv;
+ int ret;
+
+ 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");
+
+ /* Copy default settings */
+ memcpy(&priv->mux_texts, &simple_mux_texts, sizeof(priv->mux_texts));
+ memcpy(&priv->mux_enum, &simple_mux_enum, sizeof(priv->mux_enum));
+ memcpy(&priv->mux_mux, &simple_mux_mux, sizeof(priv->mux_mux));
+ memcpy(&priv->mux_widgets, &simple_mux_dapm_widgets, sizeof(priv->mux_widgets));
+ memcpy(&priv->mux_routes, &simple_mux_dapm_routes, sizeof(priv->mux_routes));
+
+ priv->mux_driver.dapm_widgets = priv->mux_widgets;
+ priv->mux_driver.num_dapm_widgets = MUX_WIDGET_SIZE;
+ priv->mux_driver.dapm_routes = priv->mux_routes;
+ priv->mux_driver.num_dapm_routes = MUX_ROUTE_SIZE;
+ priv->mux_driver.read = simple_mux_read;
+
+ /* Overwrite text ("Input 1", "Input 2") if property exists */
+ of_property_read_string_array(np, "state-labels", priv->mux_texts, MUX_TEXT_SIZE);
+
+ ret = of_property_read_u32(np, "idle-state", &priv->idle_state);
+ if (ret < 0) {
+ priv->idle_state = MUX_IDLE_AS_IS;
+ } else if (priv->idle_state != MUX_IDLE_AS_IS && priv->idle_state >= 2) {
+ dev_err(dev, "invalid idle-state %u\n", priv->idle_state);
+ return -EINVAL;
+ }
+
+ /* switch to use priv data instead of default */
+ priv->mux_enum.texts = priv->mux_texts;
+ priv->mux_mux.private_value = (unsigned long)&priv->mux_enum;
+ priv->mux_widgets[2].kcontrol_news = &priv->mux_mux;
+ priv->mux_routes[1].control = priv->mux_texts[0]; // "Input 1"
+ priv->mux_routes[2].control = priv->mux_texts[1]; // "Input 2"
+
+ return devm_snd_soc_register_component(dev, &priv->mux_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 <alexandre.belloni@bootlin.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sirf-audio-codec.c b/sound/soc/codecs/sirf-audio-codec.c
deleted file mode 100644
index 0a8e43c98a07..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.c
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * SiRF audio codec driver
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/clk.h>
-#include <linux/delay.h>
-#include <linux/io.h>
-#include <linux/regmap.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/soc.h>
-#include <sound/dmaengine_pcm.h>
-
-#include "sirf-audio-codec.h"
-
-struct sirf_audio_codec {
- struct clk *clk;
- struct regmap *regmap;
- u32 reg_ctrl0, reg_ctrl1;
-};
-
-static const char * const input_mode_mux[] = {"Single-ended",
- "Differential"};
-
-static const struct soc_enum input_mode_mux_enum =
- SOC_ENUM_SINGLE(AUDIO_IC_CODEC_CTRL1, 4, 2, input_mode_mux);
-
-static const struct snd_kcontrol_new sirf_audio_codec_input_mode_control =
- SOC_DAPM_ENUM("Route", input_mode_mux_enum);
-
-static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -12400, 100, 0);
-static const DECLARE_TLV_DB_SCALE(capture_vol_tlv_prima2, 500, 100, 0);
-static const DECLARE_TLV_DB_RANGE(capture_vol_tlv_atlas6,
- 0, 7, TLV_DB_SCALE_ITEM(-100, 100, 0),
- 0x22, 0x3F, TLV_DB_SCALE_ITEM(700, 100, 0),
-);
-
-static struct snd_kcontrol_new volume_controls_atlas6[] = {
- SOC_DOUBLE_TLV("Playback Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 16, 10,
- 0x3F, 0, capture_vol_tlv_atlas6),
-};
-
-static struct snd_kcontrol_new volume_controls_prima2[] = {
- SOC_DOUBLE_TLV("Speaker Volume", AUDIO_IC_CODEC_CTRL0, 21, 14,
- 0x7F, 0, playback_vol_tlv),
- SOC_DOUBLE_TLV("Capture Volume", AUDIO_IC_CODEC_CTRL1, 15, 10,
- 0x1F, 0, capture_vol_tlv_prima2),
-};
-
-static struct snd_kcontrol_new left_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Left Switch", AUDIO_IC_CODEC_CTRL1, 6, 1, 0),
- SOC_DAPM_SINGLE("Mic Left Switch", AUDIO_IC_CODEC_CTRL1, 3, 1, 0),
-};
-
-static struct snd_kcontrol_new right_input_path_controls[] = {
- SOC_DAPM_SINGLE("Line Right Switch", AUDIO_IC_CODEC_CTRL1, 5, 1, 0),
- SOC_DAPM_SINGLE("Mic Right Switch", AUDIO_IC_CODEC_CTRL1, 2, 1, 0),
-};
-
-static struct snd_kcontrol_new left_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 9, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 8, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_left_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 7, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_hp_right_amp_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 6, 1, 0);
-
-static struct snd_kcontrol_new left_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 11, 1, 0);
-
-static struct snd_kcontrol_new right_dac_to_speaker_lineout_switch_control =
- SOC_DAPM_SINGLE("Switch", AUDIO_IC_CODEC_CTRL0, 10, 1, 0);
-
-/* After enable adc, Delay 200ms to avoid pop noise */
-static int adc_enable_delay_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- msleep(200);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static void enable_and_reset_codec(struct regmap *regmap,
- u32 codec_enable_bits, u32 codec_reset_bits)
-{
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_enable_bits | codec_reset_bits,
- codec_enable_bits);
- msleep(20);
- regmap_update_bits(regmap, AUDIO_IC_CODEC_CTRL1,
- codec_reset_bits, codec_reset_bits);
-}
-
-static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
-#define ATLAS6_CODEC_RESET_BITS (1 << 28)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- ATLAS6_CODEC_ENABLE_BITS, ATLAS6_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, ATLAS6_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
-#define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
-#define PRIMA2_CODEC_RESET_BITS (1 << 26)
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- switch (event) {
- case SND_SOC_DAPM_POST_PMU:
- enable_and_reset_codec(sirf_audio_codec->regmap,
- PRIMA2_CODEC_ENABLE_BITS, PRIMA2_CODEC_RESET_BITS);
- break;
- case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL1, PRIMA2_CODEC_ENABLE_BITS, 0);
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-static const struct snd_soc_dapm_widget atlas6_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 26, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 27, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget prima2_output_driver_dapm_widgets[] = {
- SND_SOC_DAPM_OUT_DRV("HP Left Driver", AUDIO_IC_CODEC_CTRL1,
- 23, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP Right Driver", AUDIO_IC_CODEC_CTRL1,
- 24, 0, NULL, 0),
- SND_SOC_DAPM_OUT_DRV("Speaker Driver", AUDIO_IC_CODEC_CTRL1,
- 25, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_widget atlas6_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- atlas6_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget prima2_codec_clock_dapm_widget =
- SND_SOC_DAPM_SUPPLY("codecclk", SND_SOC_NOPM, 0, 0,
- prima2_codec_enable_and_reset_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD);
-
-static const struct snd_soc_dapm_widget sirf_audio_codec_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC left", NULL, AUDIO_IC_CODEC_CTRL0, 1, 0),
- SND_SOC_DAPM_DAC("DAC right", NULL, AUDIO_IC_CODEC_CTRL0, 0, 0),
- SND_SOC_DAPM_SWITCH("Left dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Left dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &left_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp left amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_left_amp_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to hp right amp", SND_SOC_NOPM, 0, 0,
- &right_dac_to_hp_right_amp_switch_control),
- SND_SOC_DAPM_OUT_DRV("HP amp left driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
- SND_SOC_DAPM_OUT_DRV("HP amp right driver", AUDIO_IC_CODEC_CTRL0, 3, 0,
- NULL, 0),
-
- SND_SOC_DAPM_SWITCH("Left dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &left_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_SWITCH("Right dac to speaker lineout", SND_SOC_NOPM, 0, 0,
- &right_dac_to_speaker_lineout_switch_control),
- SND_SOC_DAPM_OUT_DRV("Speaker amp driver", AUDIO_IC_CODEC_CTRL0, 4, 0,
- NULL, 0),
-
- SND_SOC_DAPM_OUTPUT("HPOUTL"),
- SND_SOC_DAPM_OUTPUT("HPOUTR"),
- SND_SOC_DAPM_OUTPUT("SPKOUT"),
-
- SND_SOC_DAPM_ADC_E("ADC left", NULL, AUDIO_IC_CODEC_CTRL1, 8, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_ADC_E("ADC right", NULL, AUDIO_IC_CODEC_CTRL1, 7, 0,
- adc_enable_delay_event, SND_SOC_DAPM_POST_PMU),
- SND_SOC_DAPM_MIXER("Left PGA mixer", AUDIO_IC_CODEC_CTRL1, 1, 0,
- &left_input_path_controls[0],
- ARRAY_SIZE(left_input_path_controls)),
- SND_SOC_DAPM_MIXER("Right PGA mixer", AUDIO_IC_CODEC_CTRL1, 0, 0,
- &right_input_path_controls[0],
- ARRAY_SIZE(right_input_path_controls)),
-
- SND_SOC_DAPM_MUX("Mic input mode mux", SND_SOC_NOPM, 0, 0,
- &sirf_audio_codec_input_mode_control),
- SND_SOC_DAPM_MICBIAS("Mic Bias", AUDIO_IC_CODEC_PWR, 3, 0),
- SND_SOC_DAPM_INPUT("MICIN1"),
- SND_SOC_DAPM_INPUT("MICIN2"),
- SND_SOC_DAPM_INPUT("LINEIN1"),
- SND_SOC_DAPM_INPUT("LINEIN2"),
-
- SND_SOC_DAPM_SUPPLY("HSL Phase Opposite", AUDIO_IC_CODEC_CTRL0,
- 30, 0, NULL, 0),
-};
-
-static const struct snd_soc_dapm_route sirf_audio_codec_map[] = {
- {"SPKOUT", NULL, "Speaker Driver"},
- {"Speaker Driver", NULL, "Speaker amp driver"},
- {"Speaker amp driver", NULL, "Left dac to speaker lineout"},
- {"Speaker amp driver", NULL, "Right dac to speaker lineout"},
- {"Left dac to speaker lineout", "Switch", "DAC left"},
- {"Right dac to speaker lineout", "Switch", "DAC right"},
- {"HPOUTL", NULL, "HP Left Driver"},
- {"HPOUTR", NULL, "HP Right Driver"},
- {"HP Left Driver", NULL, "HP amp left driver"},
- {"HP Right Driver", NULL, "HP amp right driver"},
- {"HP amp left driver", NULL, "Right dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"HP amp left driver", NULL, "Left dac to hp left amp"},
- {"HP amp right driver", NULL , "Right dac to hp right amp"},
- {"Right dac to hp left amp", "Switch", "DAC left"},
- {"Right dac to hp right amp", "Switch", "DAC right"},
- {"Left dac to hp left amp", "Switch", "DAC left"},
- {"Left dac to hp right amp", "Switch", "DAC right"},
- {"DAC left", NULL, "codecclk"},
- {"DAC right", NULL, "codecclk"},
- {"DAC left", NULL, "Playback"},
- {"DAC right", NULL, "Playback"},
- {"DAC left", NULL, "HSL Phase Opposite"},
- {"DAC right", NULL, "HSL Phase Opposite"},
-
- {"Capture", NULL, "ADC left"},
- {"Capture", NULL, "ADC right"},
- {"ADC left", NULL, "codecclk"},
- {"ADC right", NULL, "codecclk"},
- {"ADC left", NULL, "Left PGA mixer"},
- {"ADC right", NULL, "Right PGA mixer"},
- {"Left PGA mixer", "Line Left Switch", "LINEIN2"},
- {"Right PGA mixer", "Line Right Switch", "LINEIN1"},
- {"Left PGA mixer", "Mic Left Switch", "MICIN2"},
- {"Right PGA mixer", "Mic Right Switch", "Mic input mode mux"},
- {"Mic input mode mux", "Single-ended", "MICIN1"},
- {"Mic input mode mux", "Differential", "MICIN1"},
-};
-
-static void sirf_audio_codec_tx_enable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_tx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_TXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_TX_CTRL, IC_TX_ENABLE, ~IC_TX_ENABLE);
-}
-
-static void sirf_audio_codec_rx_enable(struct sirf_audio_codec *sirf_audio_codec,
- int channels)
-{
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, AUDIO_FIFO_RESET);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_RESET, ~AUDIO_FIFO_RESET);
- regmap_write(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_RXFIFO_INT_MSK, 0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP, 0);
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_PORT_IC_RXFIFO_OP,
- AUDIO_FIFO_START, AUDIO_FIFO_START);
- if (channels == 1)
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_MONO, IC_RX_ENABLE_MONO);
- else
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, IC_RX_ENABLE_STEREO);
-}
-
-static void sirf_audio_codec_rx_disable(struct sirf_audio_codec *sirf_audio_codec)
-{
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_PORT_IC_CODEC_RX_CTRL,
- IC_RX_ENABLE_STEREO, ~IC_RX_ENABLE_STEREO);
-}
-
-static int sirf_audio_codec_trigger(struct snd_pcm_substream *substream,
- int cmd,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
- int playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
-
- /*
- * This is a workaround, When stop playback,
- * need disable HP amp, avoid the current noise.
- */
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (playback) {
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, 0);
- sirf_audio_codec_tx_disable(sirf_audio_codec);
- } else
- sirf_audio_codec_rx_disable(sirf_audio_codec);
- break;
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (playback) {
- sirf_audio_codec_tx_enable(sirf_audio_codec);
- snd_soc_update_bits(codec, AUDIO_IC_CODEC_CTRL0,
- IC_HSLEN | IC_HSREN, IC_HSLEN | IC_HSREN);
- } else
- sirf_audio_codec_rx_enable(sirf_audio_codec,
- substream->runtime->channels);
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct snd_soc_dai_ops sirf_audio_codec_dai_ops = {
- .trigger = sirf_audio_codec_trigger,
-};
-
-struct snd_soc_dai_driver sirf_audio_codec_dai = {
- .name = "sirf-audio-codec",
- .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 = 1,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .ops = &sirf_audio_codec_dai_ops,
-};
-
-static int sirf_audio_codec_probe(struct snd_soc_codec *codec)
-{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
-
- pm_runtime_enable(codec->dev);
-
- if (of_device_is_compatible(codec->dev->of_node, "sirf,prima2-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- prima2_output_driver_dapm_widgets,
- ARRAY_SIZE(prima2_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &prima2_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_prima2,
- ARRAY_SIZE(volume_controls_prima2));
- }
- if (of_device_is_compatible(codec->dev->of_node, "sirf,atlas6-audio-codec")) {
- snd_soc_dapm_new_controls(dapm,
- atlas6_output_driver_dapm_widgets,
- ARRAY_SIZE(atlas6_output_driver_dapm_widgets));
- snd_soc_dapm_new_controls(dapm,
- &atlas6_codec_clock_dapm_widget, 1);
- return snd_soc_add_codec_controls(codec,
- volume_controls_atlas6,
- ARRAY_SIZE(volume_controls_atlas6));
- }
-
- return -EINVAL;
-}
-
-static int sirf_audio_codec_remove(struct snd_soc_codec *codec)
-{
- pm_runtime_disable(codec->dev);
- return 0;
-}
-
-static struct snd_soc_codec_driver soc_codec_device_sirf_audio_codec = {
- .probe = sirf_audio_codec_probe,
- .remove = sirf_audio_codec_remove,
- .dapm_widgets = sirf_audio_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sirf_audio_codec_dapm_widgets),
- .dapm_routes = sirf_audio_codec_map,
- .num_dapm_routes = ARRAY_SIZE(sirf_audio_codec_map),
- .idle_bias_off = true,
-};
-
-static const struct of_device_id sirf_audio_codec_of_match[] = {
- { .compatible = "sirf,prima2-audio-codec" },
- { .compatible = "sirf,atlas6-audio-codec" },
- {}
-};
-MODULE_DEVICE_TABLE(of, sirf_audio_codec_of_match);
-
-static const struct regmap_config sirf_audio_codec_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = AUDIO_PORT_IC_RXFIFO_INT_MSK,
- .cache_type = REGCACHE_NONE,
-};
-
-static int sirf_audio_codec_driver_probe(struct platform_device *pdev)
-{
- int ret;
- struct sirf_audio_codec *sirf_audio_codec;
- void __iomem *base;
- struct resource *mem_res;
- const struct of_device_id *match;
-
- match = of_match_node(sirf_audio_codec_of_match, pdev->dev.of_node);
-
- sirf_audio_codec = devm_kzalloc(&pdev->dev,
- sizeof(struct sirf_audio_codec), GFP_KERNEL);
- if (!sirf_audio_codec)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, sirf_audio_codec);
-
- mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- base = devm_ioremap_resource(&pdev->dev, mem_res);
- if (IS_ERR(base))
- return PTR_ERR(base);
-
- sirf_audio_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &sirf_audio_codec_regmap_config);
- if (IS_ERR(sirf_audio_codec->regmap))
- return PTR_ERR(sirf_audio_codec->regmap);
-
- sirf_audio_codec->clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(sirf_audio_codec->clk)) {
- dev_err(&pdev->dev, "Get clock failed.\n");
- return PTR_ERR(sirf_audio_codec->clk);
- }
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret) {
- dev_err(&pdev->dev, "Enable clock failed.\n");
- return ret;
- }
-
- ret = snd_soc_register_codec(&(pdev->dev),
- &soc_codec_device_sirf_audio_codec,
- &sirf_audio_codec_dai, 1);
- if (ret) {
- dev_err(&pdev->dev, "Register Audio Codec dai failed.\n");
- goto err_clk_put;
- }
-
- /*
- * Always open charge pump, if not, when the charge pump closed the
- * adc will not stable
- */
- regmap_update_bits(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- IC_CPFREQ, IC_CPFREQ);
-
- if (of_device_is_compatible(pdev->dev.of_node, "sirf,atlas6-audio-codec"))
- regmap_update_bits(sirf_audio_codec->regmap,
- AUDIO_IC_CODEC_CTRL0, IC_CPEN, IC_CPEN);
- return 0;
-
-err_clk_put:
- clk_disable_unprepare(sirf_audio_codec->clk);
- return ret;
-}
-
-static int sirf_audio_codec_driver_remove(struct platform_device *pdev)
-{
- struct sirf_audio_codec *sirf_audio_codec = platform_get_drvdata(pdev);
-
- clk_disable_unprepare(sirf_audio_codec->clk);
- snd_soc_unregister_codec(&(pdev->dev));
-
- return 0;
-}
-
-#ifdef CONFIG_PM_SLEEP
-static int sirf_audio_codec_suspend(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
-
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- &sirf_audio_codec->reg_ctrl0);
- regmap_read(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- &sirf_audio_codec->reg_ctrl1);
- clk_disable_unprepare(sirf_audio_codec->clk);
-
- return 0;
-}
-
-static int sirf_audio_codec_resume(struct device *dev)
-{
- struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(sirf_audio_codec->clk);
- if (ret)
- return ret;
-
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL0,
- sirf_audio_codec->reg_ctrl0);
- regmap_write(sirf_audio_codec->regmap, AUDIO_IC_CODEC_CTRL1,
- sirf_audio_codec->reg_ctrl1);
-
- return 0;
-}
-#endif
-
-static const struct dev_pm_ops sirf_audio_codec_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(sirf_audio_codec_suspend, sirf_audio_codec_resume)
-};
-
-static struct platform_driver sirf_audio_codec_driver = {
- .driver = {
- .name = "sirf-audio-codec",
- .of_match_table = sirf_audio_codec_of_match,
- .pm = &sirf_audio_codec_pm_ops,
- },
- .probe = sirf_audio_codec_driver_probe,
- .remove = sirf_audio_codec_driver_remove,
-};
-
-module_platform_driver(sirf_audio_codec_driver);
-
-MODULE_DESCRIPTION("SiRF audio codec driver");
-MODULE_AUTHOR("RongJun Ying <Rongjun.Ying@csr.com>");
-MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sirf-audio-codec.h b/sound/soc/codecs/sirf-audio-codec.h
deleted file mode 100644
index ba1adc03839f..000000000000
--- a/sound/soc/codecs/sirf-audio-codec.h
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * SiRF inner codec controllers define
- *
- * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
- *
- * Licensed under GPLv2 or later.
- */
-
-#ifndef _SIRF_AUDIO_CODEC_H
-#define _SIRF_AUDIO_CODEC_H
-
-
-#define AUDIO_IC_CODEC_PWR (0x00E0)
-#define AUDIO_IC_CODEC_CTRL0 (0x00E4)
-#define AUDIO_IC_CODEC_CTRL1 (0x00E8)
-#define AUDIO_IC_CODEC_CTRL2 (0x00EC)
-#define AUDIO_IC_CODEC_CTRL3 (0x00F0)
-
-#define MICBIASEN (1 << 3)
-
-#define IC_RDACEN (1 << 0)
-#define IC_LDACEN (1 << 1)
-#define IC_HSREN (1 << 2)
-#define IC_HSLEN (1 << 3)
-#define IC_SPEN (1 << 4)
-#define IC_CPEN (1 << 5)
-
-#define IC_HPRSELR (1 << 6)
-#define IC_HPLSELR (1 << 7)
-#define IC_HPRSELL (1 << 8)
-#define IC_HPLSELL (1 << 9)
-#define IC_SPSELR (1 << 10)
-#define IC_SPSELL (1 << 11)
-
-#define IC_MONOR (1 << 12)
-#define IC_MONOL (1 << 13)
-
-#define IC_RXOSRSEL (1 << 28)
-#define IC_CPFREQ (1 << 29)
-#define IC_HSINVEN (1 << 30)
-
-#define IC_MICINREN (1 << 0)
-#define IC_MICINLEN (1 << 1)
-#define IC_MICIN1SEL (1 << 2)
-#define IC_MICIN2SEL (1 << 3)
-#define IC_MICDIFSEL (1 << 4)
-#define IC_LINEIN1SEL (1 << 5)
-#define IC_LINEIN2SEL (1 << 6)
-#define IC_RADCEN (1 << 7)
-#define IC_LADCEN (1 << 8)
-#define IC_ALM (1 << 9)
-
-#define IC_DIGMICEN (1 << 22)
-#define IC_DIGMICFREQ (1 << 23)
-#define IC_ADC14B_12 (1 << 24)
-#define IC_FIRDAC_HSL_EN (1 << 25)
-#define IC_FIRDAC_HSR_EN (1 << 26)
-#define IC_FIRDAC_LOUT_EN (1 << 27)
-#define IC_POR (1 << 28)
-#define IC_CODEC_CLK_EN (1 << 29)
-#define IC_HP_3DB_BOOST (1 << 30)
-
-#define IC_ADC_LEFT_GAIN_SHIFT 16
-#define IC_ADC_RIGHT_GAIN_SHIFT 10
-#define IC_ADC_GAIN_MASK 0x3F
-#define IC_MIC_MAX_GAIN 0x39
-
-#define IC_RXPGAR_MASK 0x3F
-#define IC_RXPGAR_SHIFT 14
-#define IC_RXPGAL_MASK 0x3F
-#define IC_RXPGAL_SHIFT 21
-#define IC_RXPGAR 0x7B
-#define IC_RXPGAL 0x7B
-
-#define AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK 0x3F
-#define AUDIO_PORT_TX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_TX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_TX_FIFO_HC_OFFSET 20
-
-#define TX_FIFO_SC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_SC_OFFSET)
-#define TX_FIFO_LC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_LC_OFFSET)
-#define TX_FIFO_HC(x) (((x) & AUDIO_PORT_TX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_TX_FIFO_HC_OFFSET)
-
-#define AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK 0x0F
-#define AUDIO_PORT_RX_FIFO_SC_OFFSET 0
-#define AUDIO_PORT_RX_FIFO_LC_OFFSET 10
-#define AUDIO_PORT_RX_FIFO_HC_OFFSET 20
-
-#define RX_FIFO_SC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_SC_OFFSET)
-#define RX_FIFO_LC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_LC_OFFSET)
-#define RX_FIFO_HC(x) (((x) & AUDIO_PORT_RX_FIFO_LEVEL_CHECK_MASK) \
- << AUDIO_PORT_RX_FIFO_HC_OFFSET)
-#define AUDIO_PORT_IC_CODEC_TX_CTRL (0x00F4)
-#define AUDIO_PORT_IC_CODEC_RX_CTRL (0x00F8)
-
-#define AUDIO_PORT_IC_TXFIFO_OP (0x00FC)
-#define AUDIO_PORT_IC_TXFIFO_LEV_CHK (0x0100)
-#define AUDIO_PORT_IC_TXFIFO_STS (0x0104)
-#define AUDIO_PORT_IC_TXFIFO_INT (0x0108)
-#define AUDIO_PORT_IC_TXFIFO_INT_MSK (0x010C)
-
-#define AUDIO_PORT_IC_RXFIFO_OP (0x0110)
-#define AUDIO_PORT_IC_RXFIFO_LEV_CHK (0x0114)
-#define AUDIO_PORT_IC_RXFIFO_STS (0x0118)
-#define AUDIO_PORT_IC_RXFIFO_INT (0x011C)
-#define AUDIO_PORT_IC_RXFIFO_INT_MSK (0x0120)
-
-#define AUDIO_FIFO_START (1 << 0)
-#define AUDIO_FIFO_RESET (1 << 1)
-
-#define AUDIO_FIFO_FULL (1 << 0)
-#define AUDIO_FIFO_EMPTY (1 << 1)
-#define AUDIO_FIFO_OFLOW (1 << 2)
-#define AUDIO_FIFO_UFLOW (1 << 3)
-
-#define IC_TX_ENABLE (0x03)
-#define IC_RX_ENABLE_MONO (0x01)
-#define IC_RX_ENABLE_STEREO (0x03)
-
-#endif /*__SIRF_AUDIO_CODEC_H*/
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
new file mode 100644
index 000000000000..980c48cbc348
--- /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 <gyuhwa.park@irondevice.com>
+// Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#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"},
+ {}
+};
+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, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("Kiseok Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/sma1303.h b/sound/soc/codecs/sma1303.h
new file mode 100644
index 000000000000..ae70f207adde
--- /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 <kiseok.jo@irondevice.com>
+ *
+ */
+
+#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/sma1307.c b/sound/soc/codecs/sma1307.c
new file mode 100644
index 000000000000..b683e676640d
--- /dev/null
+++ b/sound/soc/codecs/sma1307.c
@@ -0,0 +1,2065 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// sma1307.c -- sma1307 ALSA SoC Audio driver
+//
+// Copyright 2024 Iron Device Corporation
+//
+// Auther: Gyuhwa Park <gyuwha.park@irondevice.com>
+// Auther: Kiseok Jo <kiseok.jo@irondevice.com>
+
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "sma1307.h"
+
+#define CHECK_PERIOD_TIME 1 /* sec per HZ */
+#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,\
+}
+
+static const char *setting_file = "sma1307_setting.bin";
+#define SMA1307_SETTING_CHECKSUM 0x100000
+
+/* PLL clock setting Table */
+struct sma1307_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 sma1307_data {
+ char *name;
+ void (*init)(struct regmap *regmap);
+};
+
+struct sma1307_priv {
+ bool check_fault_status;
+ bool force_mute_status;
+ bool sw_ot1_prot;
+ char *name;
+ enum sma1307_mode amp_mode;
+ int binary_mode;
+ int dapm_aif_in;
+ int dapm_aif_out0;
+ int dapm_aif_out1;
+ int dapm_sdo_en;
+ int dapm_sdo_setting;
+ int num_of_pll_matches;
+ int check_fault_period;
+ struct delayed_work check_fault_work;
+ struct device *dev;
+ struct kobject *kobj;
+ struct mutex default_lock;
+ struct regmap *regmap;
+ struct sma1307_setting_file set;
+ const struct sma1307_pll_match *pll_matches;
+ const struct sma1307_data *data;
+ unsigned int cur_vol;
+ unsigned int format;
+ unsigned int frame_size;
+ unsigned int init_vol;
+ unsigned int last_bclk;
+ unsigned int otp_trm2;
+ unsigned int otp_trm3;
+ unsigned int rev_num;
+ unsigned int sys_clk_id;
+ unsigned int tdm_slot0_rx;
+ unsigned int tdm_slot1_rx;
+ unsigned int tdm_slot0_tx;
+ unsigned int tdm_slot1_tx;
+ unsigned int tsdw_cnt;
+};
+
+static const struct sma1307_pll_match sma1307_pll_matches[] = {
+ /* in_clk_name, out_clk_name, input_clk post_n, n, vco, p_cp */
+ PLL_MATCH("1.411MHz", "24.554MHz",
+ 1411200, 0x06, 0xD1, 0x88, 0x00),
+ PLL_MATCH("1.536MHz", "24.576MHz",
+ 1536000, 0x06, 0xC0, 0x88, 0x00),
+ PLL_MATCH("2.822MHz", "24.554MHz",
+ 2822400, 0x06, 0xD1, 0x88, 0x04),
+ PLL_MATCH("3.072MHz", "24.576MHz",
+ 3072000, 0x06, 0x60, 0x88, 0x00),
+ PLL_MATCH("6.144MHz", "24.576MHz",
+ 6144000, 0x06, 0x60, 0x88, 0x04),
+ PLL_MATCH("12.288MHz", "24.576MHz",
+ 12288000, 0x06, 0x60, 0x88, 0x08),
+ PLL_MATCH("19.2MHz", "24.48MHz",
+ 19200000, 0x06, 0x7B, 0x88, 0x0C),
+ PLL_MATCH("24.576MHz", "24.576MHz",
+ 24576000, 0x06, 0x60, 0x88, 0x0C),
+};
+
+static struct snd_soc_component *sma1307_amp_component;
+
+static void sma1307_startup(struct snd_soc_component *);
+static void sma1307_shutdown(struct snd_soc_component *);
+static void sma1307_reset(struct snd_soc_component *);
+static void sma1307_set_binary(struct snd_soc_component *);
+static void sma1307_set_default(struct snd_soc_component *);
+
+/* Initial register value - 6.0W SPK (8ohm load) */
+static const struct reg_default sma1307_reg_def[] = {
+ { 0x00, 0x80 },
+ { 0x01, 0x00 },
+ { 0x02, 0x52 },
+ { 0x03, 0x4C },
+ { 0x04, 0x47 },
+ { 0x05, 0x42 },
+ { 0x06, 0x40 },
+ { 0x07, 0x40 },
+ { 0x08, 0x3C },
+ { 0x09, 0x2F },
+ { 0x0A, 0x32 },
+ { 0x0B, 0x50 },
+ { 0x0C, 0x8C },
+ { 0x0D, 0x00 },
+ { 0x0E, 0x3F },
+ { 0x0F, 0x00 },
+ { 0x10, 0x00 },
+ { 0x11, 0x00 },
+ { 0x12, 0x00 },
+ { 0x13, 0x09 },
+ { 0x14, 0x12 },
+ { 0x1C, 0x00 },
+ { 0x1D, 0x85 },
+ { 0x1E, 0xA1 },
+ { 0x1F, 0x67 },
+ { 0x22, 0x00 },
+ { 0x23, 0x1F },
+ { 0x24, 0x7A },
+ { 0x25, 0x00 },
+ { 0x26, 0xFF },
+ { 0x27, 0x39 },
+ { 0x28, 0x54 },
+ { 0x29, 0x92 },
+ { 0x2A, 0xB0 },
+ { 0x2B, 0xED },
+ { 0x2C, 0xED },
+ { 0x2D, 0xFF },
+ { 0x2E, 0xFF },
+ { 0x2F, 0xFF },
+ { 0x30, 0xFF },
+ { 0x31, 0xFF },
+ { 0x32, 0xFF },
+ { 0x34, 0x01 },
+ { 0x35, 0x17 },
+ { 0x36, 0x92 },
+ { 0x37, 0x00 },
+ { 0x38, 0x01 },
+ { 0x39, 0x10 },
+ { 0x3E, 0x01 },
+ { 0x3F, 0x08 },
+ { 0x8B, 0x05 },
+ { 0x8C, 0x50 },
+ { 0x8D, 0x80 },
+ { 0x8E, 0x10 },
+ { 0x8F, 0x02 },
+ { 0x90, 0x02 },
+ { 0x91, 0x83 },
+ { 0x92, 0xC0 },
+ { 0x93, 0x00 },
+ { 0x94, 0xA4 },
+ { 0x95, 0x74 },
+ { 0x96, 0x57 },
+ { 0xA2, 0xCC },
+ { 0xA3, 0x28 },
+ { 0xA4, 0x40 },
+ { 0xA5, 0x01 },
+ { 0xA6, 0x41 },
+ { 0xA7, 0x08 },
+ { 0xA8, 0x04 },
+ { 0xA9, 0x27 },
+ { 0xAA, 0x10 },
+ { 0xAB, 0x10 },
+ { 0xAC, 0x10 },
+ { 0xAD, 0x0F },
+ { 0xAE, 0xCD },
+ { 0xAF, 0x70 },
+ { 0xB0, 0x03 },
+ { 0xB1, 0xEF },
+ { 0xB2, 0x03 },
+ { 0xB3, 0xEF },
+ { 0xB4, 0xF3 },
+ { 0xB5, 0x3D },
+};
+
+static bool sma1307_readable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ case SMA1307_F5_READY_FOR_V_SAR:
+ case SMA1307_F7_READY_FOR_T_SAR ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_00_SYSTEM_CTRL ... SMA1307_1F_TONE_FINE_VOLUME:
+ case SMA1307_22_COMP_HYS_SEL ... SMA1307_32_BROWN_OUT_PROT19:
+ case SMA1307_34_OCP_SPK ... SMA1307_39_PMT_NZ_VAL:
+ case SMA1307_3B_TEST1 ... SMA1307_3F_ATEST2:
+ case SMA1307_8B_PLL_POST_N ... SMA1307_9A_OTP_TRM3:
+ case SMA1307_A0_PAD_CTRL0 ... SMA1307_BE_MCBS_CTRL2:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+static bool sma1307_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (reg > SMA1307_FF_DEVICE_INDEX)
+ return false;
+
+ switch (reg) {
+ case SMA1307_F8_STATUS_T1 ... SMA1307_FF_DEVICE_INDEX:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+/* DB scale conversion of speaker volume */
+static const DECLARE_TLV_DB_SCALE(sma1307_spk_tlv, -6000, 50, 0);
+
+static const char *const sma1307_aif_in_source_text[] = {
+ "Mono", "Left", "Right"
+};
+
+static const char *const sma1307_sdo_setting_text[] = {
+ "Data_One_48k", "Data_Two_48k", "Data_Two_24k",
+ "Clk_PLL", "Clk_OSC"
+};
+
+static const char *const sma1307_aif_out_source_text[] = {
+ "Disable", "After_FmtC", "After_Mixer", "After_DSP",
+ "Vrms2_Avg", "Battery", "Temperature", "After_Delay"
+};
+
+static const char *const sma1307_tdm_slot_text[] = {
+ "Slot0", "Slot1", "Slot2", "Slot3",
+ "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static const char *const sma1307_binary_mode_text[] = {
+ "Mode0", "Mode1", "Mode2", "Mode3", "Mode4"
+};
+
+static const char *const sma1307_reset_text[] = {
+ "Reset"
+};
+
+static const struct soc_enum sma1307_aif_in_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_in_source_text),
+ sma1307_aif_in_source_text);
+static const struct soc_enum sma1307_sdo_setting_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_sdo_setting_text),
+ sma1307_sdo_setting_text);
+static const struct soc_enum sma1307_aif_out_source_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_aif_out_source_text),
+ sma1307_aif_out_source_text);
+static const struct soc_enum sma1307_tdm_slot_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_tdm_slot_text),
+ sma1307_tdm_slot_text);
+static const struct soc_enum sma1307_binary_mode_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_binary_mode_text),
+ sma1307_binary_mode_text);
+static const struct soc_enum sma1307_reset_enum =
+SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sma1307_reset_text),
+ sma1307_reset_text);
+
+static int sma1307_force_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->force_mute_status;
+
+ return 0;
+}
+
+static int sma1307_force_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->force_mute_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->force_mute_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_tdm_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val1, val2;
+
+ regmap_read(sma1307->regmap, SMA1307_A5_TDM1, &val1);
+ regmap_read(sma1307->regmap, SMA1307_A6_TDM2, &val2);
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val1 & SMA1307_TDM_SLOT0_RX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val1 & SMA1307_TDM_SLOT1_RX_POS_MASK;
+ sma1307->tdm_slot1_rx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = (val2 & SMA1307_TDM_SLOT0_TX_POS_MASK) >> 3;
+ sma1307->tdm_slot0_tx = ucontrol->value.integer.value[0];
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ ucontrol->value.integer.value[0]
+ = val2 & SMA1307_TDM_SLOT1_TX_POS_MASK;
+ sma1307->tdm_slot1_tx = ucontrol->value.integer.value[0];
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_tdm_slot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_RX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_rx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_rx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK, val);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX0_POS_NAME)) {
+ if (sma1307->tdm_slot0_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot0_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK, val << 3);
+ }
+ } else if (!strcmp(kcontrol->id.name, SMA1307_TDM_TX1_POS_NAME)) {
+ if (sma1307->tdm_slot1_tx == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->tdm_slot1_tx = val;
+ regmap_update_bits(sma1307->regmap, SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK, val);
+ }
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ return change;
+}
+
+static int sma1307_sw_ot1_prot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->sw_ot1_prot;
+
+ return 0;
+}
+
+static int sma1307_sw_ot1_prot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->sw_ot1_prot == val)
+ change = false;
+ else {
+ change = true;
+ sma1307->sw_ot1_prot = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = (int)sma1307->check_fault_status;
+
+ return 0;
+}
+
+static int sma1307_check_fault_status_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ bool change = false, val = (bool)ucontrol->value.integer.value[0];
+
+ if (sma1307->check_fault_status == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_status = val;
+ }
+
+ return change;
+}
+
+static int sma1307_check_fault_period_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sma1307->check_fault_period;
+
+ return 0;
+}
+
+static int sma1307_check_fault_period_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ bool change = false;
+ int val = ucontrol->value.integer.value[0];
+
+ if (val < mc->min || val > mc->max)
+ return -EINVAL;
+ if (sma1307->check_fault_period == val) {
+ change = false;
+ } else {
+ change = true;
+ sma1307->check_fault_period = val;
+ }
+
+ return change;
+}
+
+static int sma1307_reset_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_RESET_MASK, SMA1307_RESET_ON);
+ sma1307_reset(component);
+
+ snd_ctl_notify(component->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE,
+ &kcontrol->id);
+
+ return true;
+}
+
+static int sma1307_binary_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_kcontrol_component(kcontrol);
+ struct sma1307_priv *sma1307 = snd_kcontrol_chip(kcontrol);
+
+ sma1307->binary_mode = (int)ucontrol->value.enumerated.item[0];
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+
+ return snd_soc_put_enum_double(kcontrol, ucontrol);
+}
+
+static void sma1307_startup(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_ON);
+
+ if (sma1307->amp_mode == SMA1307_MONO_MODE) {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_MONO);
+ } else {
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK,
+ SMA1307_SPK_STEREO);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_shutdown(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ /* for SMA1307A */
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK, SMA1307_SPK_MUTE);
+ /* Need to wait time for mute slope */
+ msleep(55);
+
+ regmap_update_bits(sma1307->regmap, SMA1307_10_SYSTEM_CTRL1,
+ SMA1307_SPK_MODE_MASK, SMA1307_SPK_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_OFF);
+ regmap_update_bits(sma1307->regmap, SMA1307_00_SYSTEM_CTRL,
+ SMA1307_POWER_MASK, SMA1307_POWER_OFF);
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_aif_in;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_MONO_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_ON);
+ break;
+ case SMA1307_LEFT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_NORMAL);
+ break;
+ case SMA1307_RIGHT_MODE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_MONOMIX_MASK,
+ SMA1307_MONOMIX_OFF);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_11_SYSTEM_CTRL2,
+ SMA1307_LR_DATA_SW_MASK,
+ SMA1307_LR_DATA_SW_SWAP);
+ break;
+ default:
+
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ sma1307->amp_mode = mux;
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_sdo_setting_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = sma1307->dapm_sdo_setting;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (mux) {
+ case SMA1307_OUT_DATA_ONE_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_ONE_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_48K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_OUTPUT3_DIS
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_DATA_TWO_24K:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_SDO_OUTPUT2_MASK,
+ SMA1307_TWO_SDO_PER_CH);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT3_MASK
+ |
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_TWO_SDO_PER_CH_24K
+ | SMA1307_SDO_DATA);
+ break;
+ case SMA1307_OUT_CLK_PLL:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_PLL);
+
+ break;
+ case SMA1307_OUT_CLK_OSC:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_DATA_CLK_SEL_MASK,
+ SMA1307_SDO_CLK_OSC);
+
+ break;
+ default:
+ dev_err(sma1307->dev, "%s: Invalid value (%d)\n",
+ __func__, mux);
+ return -EINVAL;
+ }
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int mux = 0, val = 0, mask = 0;
+
+ if (!strcmp(w->name, SMA1307_AIF_OUT0_NAME)) {
+ mux = sma1307->dapm_aif_out0;
+ val = mux;
+ mask = SMA1307_SDO_OUT0_SEL_MASK;
+ } else if (!strcmp(w->name, SMA1307_AIF_OUT1_NAME)) {
+ mux = sma1307->dapm_aif_out1;
+ val = mux << 3;
+ mask = SMA1307_SDO_OUT1_SEL_MASK;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid widget - %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap, SMA1307_09_OUTPUT_CTRL,
+ mask, val);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_OUTPUT_PORT_ENABLE);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_LOGIC_OUTPUT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_09_OUTPUT_CTRL,
+ SMA1307_PORT_CONFIG_MASK,
+ SMA1307_INPUT_PORT_ONLY);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A3_TOP_MAN2,
+ SMA1307_SDO_OUTPUT_MASK,
+ SMA1307_HIGH_Z_OUTPUT);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_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:
+ sma1307_startup(component);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ sma1307_shutdown(component);
+ break;
+ }
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.enumerated.item[0] = (unsigned int)sma1307->dapm_aif_in;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_in_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_in_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_aif_in != val) {
+ change = true;
+ sma1307->dapm_aif_in = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_setting_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.enumerated.item[0] =
+ (unsigned int)sma1307->dapm_sdo_setting;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_setting_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_sdo_setting_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_setting != val) {
+ change = true;
+ sma1307->dapm_sdo_setting = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_aif_out_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ unsigned int val = 0;
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out0;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ val = (unsigned int)sma1307->dapm_aif_out1;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+ ucontrol->value.enumerated.item[0] = val;
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_aif_out_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.enumerated.item[0];
+ bool change;
+
+ if ((val < 0) || (val >= ARRAY_SIZE(sma1307_aif_out_source_text))) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT0_NAME)) {
+ if (sma1307->dapm_aif_out0 != val) {
+ change = true;
+ sma1307->dapm_aif_out0 = val;
+ } else
+ change = false;
+ } else if (!strcmp(kcontrol->id.name, SMA1307_AIF_OUT1_NAME)) {
+ if (sma1307->dapm_aif_out1 != val) {
+ change = true;
+ sma1307->dapm_aif_out1 = val;
+ } else
+ change = false;
+ } else {
+ dev_err(sma1307->dev, "%s: Invalid Control ID - %s\n",
+ __func__, kcontrol->id.name);
+ return -EINVAL;
+ }
+
+ snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+ return change;
+}
+
+static int sma1307_dapm_sdo_enable_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+
+ ucontrol->value.integer.value[0] = (long)sma1307->dapm_sdo_en;
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return 0;
+}
+
+static int sma1307_dapm_sdo_enable_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct sma1307_priv *sma1307 =
+ snd_soc_component_get_drvdata(dapm->component);
+ int val = (int)ucontrol->value.integer.value[0];
+ bool change;
+
+ if ((val < 0) || (val > 1)) {
+ dev_err(sma1307->dev, "%s: Out of range\n", __func__);
+ return -EINVAL;
+ }
+
+ if (sma1307->dapm_sdo_en != val) {
+ change = true;
+ sma1307->dapm_sdo_en = val;
+ } else
+ change = false;
+
+ snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+ return change;
+}
+
+static const struct snd_kcontrol_new sma1307_aif_in_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_IN_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_in_get,
+ .put = sma1307_dapm_aif_in_put,
+ .private_value = (unsigned long)&sma1307_aif_in_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_setting_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "SDO Setting",
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_sdo_setting_get,
+ .put = sma1307_dapm_sdo_setting_put,
+ .private_value = (unsigned long)&sma1307_sdo_setting_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out0_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT0_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_aif_out1_source_control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = SMA1307_AIF_OUT1_NAME,
+ .info = snd_soc_info_enum_double,
+ .get = sma1307_dapm_aif_out_get,
+ .put = sma1307_dapm_aif_out_put,
+ .private_value = (unsigned long)&sma1307_aif_out_source_enum
+};
+
+static const struct snd_kcontrol_new sma1307_sdo_control =
+ SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, 0, 1, 0,
+ sma1307_dapm_sdo_enable_get, sma1307_dapm_sdo_enable_put);
+
+static const struct snd_kcontrol_new sma1307_enable_control =
+ SOC_DAPM_SINGLE("Switch", SMA1307_00_SYSTEM_CTRL, 0, 1, 0);
+
+static const struct snd_kcontrol_new sma1307_binary_mode_control[] = {
+ SOC_ENUM_EXT("Binary Mode", sma1307_binary_mode_enum,
+ snd_soc_get_enum_double, sma1307_binary_mode_put),
+};
+
+static const struct snd_kcontrol_new sma1307_snd_controls[] = {
+ SOC_SINGLE_TLV(SMA1307_VOL_CTRL_NAME, SMA1307_0A_SPK_VOL,
+ 0, 167, 1, sma1307_spk_tlv),
+ SOC_ENUM_EXT(SMA1307_TDM_RX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_RX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX0_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_TDM_TX1_POS_NAME, sma1307_tdm_slot_enum,
+ sma1307_tdm_slot_get, sma1307_tdm_slot_put),
+ SOC_ENUM_EXT(SMA1307_RESET_CTRL_NAME, sma1307_reset_enum,
+ snd_soc_get_enum_double, sma1307_reset_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_FORCE_MUTE_CTRL_NAME, 0,
+ sma1307_force_mute_get, sma1307_force_mute_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_OT1_SW_PROT_CTRL_NAME, 0,
+ sma1307_sw_ot1_prot_get, sma1307_sw_ot1_prot_put),
+ SOC_SINGLE_BOOL_EXT(SMA1307_CHECK_FAULT_STATUS_NAME, 0,
+ sma1307_check_fault_status_get,
+ sma1307_check_fault_status_put),
+ SOC_SINGLE_EXT(SMA1307_CHECK_FAULT_PERIOD_NAME, SND_SOC_NOPM, 0, 600, 0,
+ sma1307_check_fault_period_get,
+ sma1307_check_fault_period_put),
+};
+
+static const struct snd_soc_dapm_widget sma1307_dapm_widgets[] = {
+ /* platform domain */
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("SDO"),
+
+ /* path domain */
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_IN_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_in_source_control,
+ sma1307_aif_in_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("SDO Setting", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_setting_control,
+ sma1307_sdo_setting_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT0_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out0_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_MUX_E(SMA1307_AIF_OUT1_NAME, SND_SOC_NOPM, 0, 0,
+ &sma1307_aif_out1_source_control,
+ sma1307_aif_out_event,
+ SND_SOC_DAPM_PRE_PMU),
+ SND_SOC_DAPM_SWITCH_E("SDO Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_sdo_control,
+ sma1307_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_OUT_DRV_E("AMP Power", SND_SOC_NOPM, 0, 0, NULL, 0,
+ sma1307_power_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 0,
+ &sma1307_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 sma1307_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" },
+
+ { "SDO Setting", "Data_One_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_48k", "SDO Enable" },
+ { "SDO Setting", "Data_Two_24k", "SDO Enable" },
+ { "SDO Setting", "Clk_PLL", "SDO Enable" },
+ { "SDO Setting", "Clk_OSC", "SDO Enable" },
+
+ { "AIF OUT0 Source", "Disable", "SDO Setting" },
+ { "AIF OUT0 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT0 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT0 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT0 Source", "Battery", "SDO Setting" },
+ { "AIF OUT0 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT0 Source", "After_Delay", "SDO Setting" },
+
+ { "AIF OUT1 Source", "Disable", "SDO Setting" },
+ { "AIF OUT1 Source", "After_FmtC", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Mixer", "SDO Setting" },
+ { "AIF OUT1 Source", "After_DSP", "SDO Setting" },
+ { "AIF OUT1 Source", "Vrms2_Avg", "SDO Setting" },
+ { "AIF OUT1 Source", "Battery", "SDO Setting" },
+ { "AIF OUT1 Source", "Temperature", "SDO Setting" },
+ { "AIF OUT1 Source", "After_Delay", "SDO Setting" },
+
+ { "Entry", NULL, "AIF OUT0 Source" },
+ { "Entry", NULL, "AIF OUT1 Source" },
+ { "Entry", NULL, "AIF IN Source" },
+
+ { "AMP Power", NULL, "Entry" },
+
+ { "AMP Enable", "Switch", "AMP Power" },
+ { "SPK", NULL, "AMP Enable" },
+
+ /* Capture */
+ { "AIF OUT", NULL, "AMP Enable" },
+};
+
+static void sma1307_setup_pll(struct snd_soc_component *component,
+ unsigned int bclk)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ int i = 0;
+
+ dev_dbg(component->dev, "%s: BCLK = %dHz\n", __func__, bclk);
+
+ if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_MCLK) {
+ dev_warn(component->dev, "%s: MCLK is not supported\n",
+ __func__);
+ } else if (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ for (i = 0; i < sma1307->num_of_pll_matches; i++) {
+ if (sma1307->pll_matches[i].input_clk == bclk)
+ break;
+ }
+ if (i == sma1307->num_of_pll_matches) {
+ dev_warn(component->dev,
+ "%s: No matching value between pll table and SCK\n",
+ __func__);
+ return;
+ }
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A2_TOP_MAN1,
+ SMA1307_PLL_MASK, SMA1307_PLL_ON);
+ }
+
+ regmap_write(sma1307->regmap, SMA1307_8B_PLL_POST_N,
+ sma1307->pll_matches[i].post_n);
+ regmap_write(sma1307->regmap, SMA1307_8C_PLL_N,
+ sma1307->pll_matches[i].n);
+ regmap_write(sma1307->regmap, SMA1307_8D_PLL_A_SETTING,
+ sma1307->pll_matches[i].vco);
+ regmap_write(sma1307->regmap, SMA1307_8E_PLL_P_CP,
+ sma1307->pll_matches[i].p_cp);
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int bclk = 0;
+
+ if (sma1307->format == SND_SOC_DAIFMT_DSP_A)
+ bclk = params_rate(params) * sma1307->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 (sma1307->sys_clk_id == SMA1307_PLL_CLKIN_BCLK) {
+ if (sma1307->last_bclk != bclk) {
+ sma1307_setup_pll(component, bclk);
+ sma1307->last_bclk = bclk;
+ }
+ }
+
+ switch (params_rate(params)) {
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 44100:
+ case 48000:
+ break;
+
+ case 96000:
+ dev_warn(component->dev,
+ "%s: %d rate not support SDO\n", __func__,
+ params_rate(params));
+ break;
+
+ default:
+ dev_err(component->dev, "%s: not support rate : %d\n",
+ __func__, params_rate(params));
+
+ return -EINVAL;
+ }
+
+ /* substream->stream is SNDRV_PCM_STREAM_CAPTURE */
+ } else {
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_32FS |
+ SMA1307_DATA_16BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_SCK_RATE_MASK
+ |
+ SMA1307_DATA_WIDTH_MASK,
+ SMA1307_SCK_64FS |
+ SMA1307_DATA_24BIT);
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: not support data bit : %d\n", __func__,
+ params_format(params));
+ return -EINVAL;
+ }
+ }
+
+ switch (sma1307->format) {
+ case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_I2S_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK, SMA1307_LJ);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_LJ_FORMAT);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ switch (params_width(params)) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_16BIT);
+ break;
+ case 24:
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_RJ_24BIT);
+ break;
+ }
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_I2S_MODE_MASK,
+ SMA1307_STANDARD_I2S);
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK,
+ SMA1307_TDM_FORMAT);
+ 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;
+ }
+
+ return 0;
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ switch (clk_id) {
+ case SMA1307_EXTERNAL_CLOCK_19_2:
+ case SMA1307_EXTERNAL_CLOCK_24_576:
+ case SMA1307_PLL_CLKIN_MCLK:
+ case SMA1307_PLL_CLKIN_BCLK:
+ break;
+ default:
+ dev_err(component->dev, "%s: Invalid clk id: %d\n",
+ __func__, clk_id);
+ return -EINVAL;
+ }
+ sma1307->sys_clk_id = clk_id;
+
+ return 0;
+}
+
+static int sma1307_dai_set_fmt_amp(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ 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");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_DEVICE_MODE);
+ break;
+
+ case SND_SOC_DAIFMT_CBP_CFP:
+ dev_dbg(component->dev,
+ "%s: %s\n", __func__, "I2S/TDM Controller mode");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_CONTROLLER_DEVICE_MASK,
+ SMA1307_CONTROLLER_MODE);
+ break;
+
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Controller/Device : 0x%x\n",
+ __func__, 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:
+ sma1307->format = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ break;
+ default:
+ dev_err(component->dev,
+ "%s: Unsupported Audio Interface Format : 0x%x\n",
+ __func__, 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");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_SCK_RISING_MASK,
+ SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Invert BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK
+ | SMA1307_SCK_RISING_MASK,
+ SMA1307_HIGH_FIRST_CH
+ | SMA1307_SCK_RISING_EDGE);
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ dev_dbg(component->dev, "%s: %s\n",
+ __func__, "Normal BCLK + Invert Frame");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_01_INPUT_CTRL1,
+ SMA1307_LEFTPOL_MASK,
+ SMA1307_HIGH_FIRST_CH);
+ 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,
+ "%s: Unsupported Bit & Frameclock : 0x%x\n",
+ __func__, fmt);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sma1307_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 sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(component->dev, "%s: slots = %d, slot_width - %d\n",
+ __func__, slots, slot_width);
+
+ sma1307->frame_size = slot_width * slots;
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A4_TOP_MAN3,
+ SMA1307_INTERFACE_MASK, SMA1307_TDM_FORMAT);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_TX_MODE_MASK,
+ SMA1307_TDM_TX_MONO);
+
+ switch (slot_width) {
+ case 16:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_16);
+ break;
+ case 32:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_DL_MASK,
+ SMA1307_TDM_DL_32);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slot_width\n",
+ __func__, slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 4:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_4);
+ break;
+ case 8:
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_N_SLOT_MASK,
+ SMA1307_TDM_N_SLOT_8);
+ break;
+ default:
+ dev_err(component->dev, "%s: not support TDM %d slots\n",
+ __func__, slots);
+ return -EINVAL;
+ }
+
+ if (sma1307->tdm_slot0_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT0_RX_POS_MASK,
+ sma1307->tdm_slot0_rx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-rx %d set\n",
+ __func__, sma1307->tdm_slot0_rx);
+
+ if (sma1307->tdm_slot1_rx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A5_TDM1,
+ SMA1307_TDM_SLOT1_RX_POS_MASK,
+ sma1307->tdm_slot1_rx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-rx %d set\n",
+ __func__, sma1307->tdm_slot1_rx);
+
+ if (sma1307->tdm_slot0_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT0_TX_POS_MASK,
+ sma1307->tdm_slot0_tx << 3);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot0-tx %d set\n",
+ __func__, sma1307->tdm_slot0_tx);
+
+ if (sma1307->tdm_slot1_tx < slots)
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_A6_TDM2,
+ SMA1307_TDM_SLOT1_TX_POS_MASK,
+ sma1307->tdm_slot1_tx);
+ else
+ dev_err(component->dev, "%s: Incorrect tdm-slot1-tx %d set\n",
+ __func__, sma1307->tdm_slot1_tx);
+
+ return 0;
+}
+
+static int sma1307_dai_mute_stream(struct snd_soc_dai *dai, int mute,
+ int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+
+ if (stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
+ if (mute) {
+ dev_dbg(component->dev, "%s: %s\n", __func__, "MUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_MUTE);
+ } else {
+ if (!sma1307->force_mute_status) {
+ dev_dbg(component->dev, "%s: %s\n", __func__,
+ "UNMUTE");
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_0E_MUTE_VOL_CTRL,
+ SMA1307_SPK_MUTE_MASK,
+ SMA1307_SPK_UNMUTE);
+ } else {
+ dev_dbg(sma1307->dev, "%s: FORCE MUTE!!!\n", __func__);
+ }
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops sma1307_dai_ops_amp = {
+ .hw_params = sma1307_dai_hw_params_amp,
+ .set_fmt = sma1307_dai_set_fmt_amp,
+ .set_sysclk = sma1307_dai_set_sysclk_amp,
+ .set_tdm_slot = sma1307_dai_set_tdm_slot,
+ .mute_stream = sma1307_dai_mute_stream,
+};
+
+#define SMA1307_RATES_PLAYBACK SNDRV_PCM_RATE_8000_96000
+#define SMA1307_RATES_CAPTURE SNDRV_PCM_RATE_8000_48000
+#define SMA1307_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver sma1307_dai[] = {
+ {
+ .name = "sma1307-amplifier",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_PLAYBACK,
+ .formats = SMA1307_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SMA1307_RATES_CAPTURE,
+ .formats = SMA1307_FORMATS,
+ },
+ .ops = &sma1307_dai_ops_amp,
+ },
+};
+
+static void sma1307_check_fault_worker(struct work_struct *work)
+{
+ struct sma1307_priv *sma1307 =
+ container_of(work, struct sma1307_priv, check_fault_work.work);
+ unsigned int status1_val, status2_val;
+ char *envp[3] = { NULL, NULL, NULL };
+
+ if (sma1307->tsdw_cnt)
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->cur_vol);
+ else
+ regmap_read(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, &sma1307->init_vol);
+
+ regmap_read(sma1307->regmap, SMA1307_FA_STATUS1, &status1_val);
+ regmap_read(sma1307->regmap, SMA1307_FB_STATUS2, &status2_val);
+
+ if (~status1_val & SMA1307_OT1_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT1(Over Temperature Level 1)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1");
+ if (sma1307->sw_ot1_prot) {
+ /* Volume control (Current Volume -3dB) */
+ if ((sma1307->cur_vol + 6) <= 0xFA) {
+ sma1307->cur_vol += 6;
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL,
+ sma1307->cur_vol);
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+ }
+ sma1307->tsdw_cnt++;
+ } else if (sma1307->tsdw_cnt) {
+ regmap_write(sma1307->regmap,
+ SMA1307_0A_SPK_VOL, sma1307->init_vol);
+ sma1307->tsdw_cnt = 0;
+ sma1307->cur_vol = sma1307->init_vol;
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR");
+ envp[1] = kasprintf(GFP_KERNEL,
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ }
+
+ if (~status1_val & SMA1307_OT2_OK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OT2(Over Temperature Level 2)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2");
+ }
+ if (status1_val & SMA1307_UVLO_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: UVLO(Under Voltage Lock Out)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO");
+ }
+ if (status1_val & SMA1307_OVP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OVP_BST(Over Voltage Protection)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST");
+ }
+ if (status2_val & SMA1307_OCP_SPK_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_SPK(Over Current Protect SPK)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK");
+ }
+ if (status2_val & SMA1307_OCP_BST_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: OCP_BST(Over Current Protect Boost)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST");
+ }
+ if (status2_val & SMA1307_CLK_MON_STATUS) {
+ dev_crit(sma1307->dev,
+ "%s: CLK_FAULT(No clock input)\n", __func__);
+ envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT");
+ }
+
+ if (envp[0] != NULL) {
+ if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp))
+ dev_err(sma1307->dev,
+ "%s: Error sending uevent\n", __func__);
+ kfree(envp[0]);
+ kfree(envp[1]);
+ }
+
+ if (sma1307->check_fault_status) {
+ if (sma1307->check_fault_period > 0)
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ sma1307->check_fault_period * HZ);
+ else
+ queue_delayed_work(system_freezable_wq,
+ &sma1307->check_fault_work,
+ CHECK_PERIOD_TIME * HZ);
+ }
+}
+
+static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *file)
+{
+ const struct firmware *fw;
+ int size, offset, num_mode;
+ int ret;
+
+ ret = request_firmware(&fw, file, sma1307->dev);
+
+ if (ret) {
+ dev_err(sma1307->dev, "%s: failed to read \"%s\": %pe\n",
+ __func__, setting_file, ERR_PTR(ret));
+ sma1307->set.status = false;
+ return;
+ } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) {
+ dev_err(sma1307->dev, "%s: Invalid file\n", __func__);
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+
+ int *data __free(kfree) = kzalloc(fw->size, GFP_KERNEL);
+ if (!data) {
+ release_firmware(fw);
+ sma1307->set.status = false;
+ return;
+ }
+ size = fw->size >> 2;
+ memcpy(data, fw->data, fw->size);
+
+ release_firmware(fw);
+
+ /* HEADER */
+ sma1307->set.header_size = SMA1307_SETTING_HEADER_SIZE;
+ sma1307->set.checksum = data[sma1307->set.header_size - 2];
+ sma1307->set.num_mode = data[sma1307->set.header_size - 1];
+ num_mode = sma1307->set.num_mode;
+ sma1307->set.header = devm_kmalloc_array(sma1307->dev,
+ sma1307->set.header_size,
+ sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.header) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.header, data,
+ sma1307->set.header_size * sizeof(int));
+
+ if ((sma1307->set.checksum >> 8) != SMA1307_SETTING_CHECKSUM) {
+ dev_err(sma1307->dev, "%s: checksum failed \"%s\"\n",
+ __func__, setting_file);
+ sma1307->set.status = false;
+ return;
+ }
+
+ /* DEFAULT */
+ sma1307->set.def_size = SMA1307_SETTING_DEFAULT_SIZE;
+ sma1307->set.def
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.def_size * sizeof(int), GFP_KERNEL);
+ if (!sma1307->set.def) {
+ sma1307->set.status = false;
+ return;
+ }
+
+ memcpy(sma1307->set.def,
+ &data[sma1307->set.header_size],
+ sma1307->set.def_size * sizeof(int));
+
+ /* MODE */
+ offset = sma1307->set.header_size + sma1307->set.def_size;
+ sma1307->set.mode_size = DIV_ROUND_CLOSEST(size - offset, num_mode + 1);
+ for (int i = 0; i < num_mode; i++) {
+ sma1307->set.mode_set[i]
+ = devm_kzalloc(sma1307->dev,
+ sma1307->set.mode_size * 2 * sizeof(int),
+ GFP_KERNEL);
+ if (!sma1307->set.mode_set[i]) {
+ for (int j = 0; j < i; j++)
+ kfree(sma1307->set.mode_set[j]);
+ sma1307->set.status = false;
+ return;
+ }
+
+ for (int j = 0; j < sma1307->set.mode_size; j++) {
+ sma1307->set.mode_set[i][2 * j]
+ = data[offset + ((num_mode + 1) * j)];
+ sma1307->set.mode_set[i][2 * j + 1]
+ = data[offset + ((num_mode + 1) * j + i + 1)];
+ }
+ }
+
+ sma1307->set.status = true;
+
+}
+
+static void sma1307_reset(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ unsigned int status = 0;
+
+ regmap_read(sma1307->regmap, SMA1307_FF_DEVICE_INDEX, &status);
+
+ sma1307->rev_num = status & SMA1307_REV_NUM_STATUS;
+ dev_dbg(component->dev, "%s: SMA1307 Revision %d\n",
+ __func__, sma1307->rev_num);
+ regmap_read(sma1307->regmap, SMA1307_99_OTP_TRM2, &sma1307->otp_trm2);
+ regmap_read(sma1307->regmap, SMA1307_9A_OTP_TRM3, &sma1307->otp_trm3);
+
+ if ((sma1307->otp_trm2 & SMA1307_OTP_STAT_MASK) != SMA1307_OTP_STAT_1)
+ dev_warn(component->dev, "%s: SMA1307 OTP Status Fail\n",
+ __func__);
+
+ /* Register Initial Value Setting */
+ sma1307_setting_loaded(sma1307, setting_file);
+ if (sma1307->set.status)
+ sma1307_set_binary(component);
+ else
+ sma1307_set_default(component);
+
+ regmap_update_bits(sma1307->regmap,
+ SMA1307_93_INT_CTRL,
+ SMA1307_DIS_INT_MASK, SMA1307_HIGH_Z_INT);
+ regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->init_vol);
+}
+
+static void sma1307_set_binary(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0, mode = 0;
+
+ for (i = 0; i < (sma1307->set.def_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ regmap_write(sma1307->regmap, i, sma1307->set.def[i]);
+
+ }
+ }
+ for (i = 0; i < (sma1307->set.mode_size); i++) {
+ if (sma1307_writeable_register(sma1307->dev, i)
+ && ((i < SMA1307_97_OTP_TRM0)
+ || (i > SMA1307_9A_OTP_TRM3))) {
+ mode = sma1307->binary_mode;
+ regmap_write(sma1307->regmap,
+ sma1307->set.mode_set[mode][2 * i],
+ sma1307->set.mode_set[mode][2 * i +
+ 1]);
+ }
+ }
+}
+
+static void sma1307_set_default(struct snd_soc_component *component)
+{
+ struct sma1307_priv *sma1307 = snd_soc_component_get_drvdata(component);
+ int i = 0;
+
+ for (i = 0; i < (unsigned int)ARRAY_SIZE(sma1307_reg_def); i++)
+ regmap_write(sma1307->regmap,
+ sma1307_reg_def[i].reg,
+ sma1307_reg_def[i].def);
+
+ if (!strcmp(sma1307->name, DEVICE_NAME_SMA1307AQ))
+ sma1307->data->init(sma1307->regmap);
+}
+
+static int sma1307_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(component);
+
+ snd_soc_dapm_sync(dapm);
+
+ sma1307_amp_component = component;
+
+ snd_soc_add_component_controls(component, sma1307_binary_mode_control,
+ ARRAY_SIZE(sma1307_binary_mode_control));
+ sma1307_reset(component);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver sma1307_component = {
+ .probe = sma1307_probe,
+ .controls = sma1307_snd_controls,
+ .num_controls = ARRAY_SIZE(sma1307_snd_controls),
+ .dapm_widgets = sma1307_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sma1307_dapm_widgets),
+ .dapm_routes = sma1307_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sma1307_audio_map),
+};
+
+static const struct regmap_config sma_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SMA1307_FF_DEVICE_INDEX,
+ .readable_reg = sma1307_readable_register,
+ .writeable_reg = sma1307_writeable_register,
+ .volatile_reg = sma1307_volatile_register,
+
+ .reg_defaults = sma1307_reg_def,
+ .num_reg_defaults = ARRAY_SIZE(sma1307_reg_def),
+};
+
+static void sma1307aq_init(struct regmap *regmap)
+{
+ /* Guidelines for driving 4ohm load */
+ /* Brown Out Protection */
+ regmap_write(regmap, SMA1307_02_BROWN_OUT_PROT1, 0x62);
+ regmap_write(regmap, SMA1307_03_BROWN_OUT_PROT2, 0x5D);
+ regmap_write(regmap, SMA1307_04_BROWN_OUT_PROT3, 0x57);
+ regmap_write(regmap, SMA1307_05_BROWN_OUT_PROT8, 0x54);
+ regmap_write(regmap, SMA1307_06_BROWN_OUT_PROT9, 0x51);
+ regmap_write(regmap,
+ SMA1307_07_BROWN_OUT_PROT10, 0x4D);
+ regmap_write(regmap,
+ SMA1307_08_BROWN_OUT_PROT11, 0x4B);
+ regmap_write(regmap, SMA1307_27_BROWN_OUT_PROT4, 0x3C);
+ regmap_write(regmap, SMA1307_28_BROWN_OUT_PROT5, 0x5B);
+ regmap_write(regmap,
+ SMA1307_29_BROWN_OUT_PROT12, 0x78);
+ regmap_write(regmap,
+ SMA1307_2A_BROWN_OUT_PROT13, 0x96);
+ regmap_write(regmap,
+ SMA1307_2B_BROWN_OUT_PROT14, 0xB4);
+ regmap_write(regmap,
+ SMA1307_2C_BROWN_OUT_PROT15, 0xD3);
+ /* FDPEC Gain */
+ regmap_write(regmap, SMA1307_35_FDPEC_CTRL0, 0x16);
+ /* FLT Vdd */
+ regmap_write(regmap, SMA1307_92_FDPEC_CTRL1, 0xA0);
+ /* Boost Max */
+ regmap_write(regmap, SMA1307_AB_BOOST_CTRL4, 0x0F);
+}
+
+static const struct sma1307_data sma1307aq_data = {
+ .name = DEVICE_NAME_SMA1307AQ,
+ .init = sma1307aq_init,
+};
+
+static int sma1307_i2c_probe(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307;
+ const struct sma1307_data *data;
+ int ret = 0;
+ unsigned int device_info;
+
+ sma1307 = devm_kzalloc(&client->dev,
+ sizeof(*sma1307), GFP_KERNEL);
+ if (!sma1307)
+ return -ENOMEM;
+
+ sma1307->regmap = devm_regmap_init_i2c(client, &sma_i2c_regmap);
+ if (IS_ERR(sma1307->regmap)) {
+ return dev_err_probe(&client->dev, PTR_ERR(sma1307->regmap),
+ "%s: failed to allocate register map\n", __func__);
+ }
+
+ data = device_get_match_data(&client->dev);
+ if (!data)
+ return -ENODEV;
+
+ sma1307->data = data;
+
+ /* set initial value as normal AMP IC status */
+ sma1307->name = client->name;
+ sma1307->format = SND_SOC_DAIFMT_I2S;
+ sma1307->sys_clk_id = SMA1307_PLL_CLKIN_BCLK;
+ sma1307->num_of_pll_matches = ARRAY_SIZE(sma1307_pll_matches);
+
+ sma1307->check_fault_period = CHECK_PERIOD_TIME;
+ sma1307->check_fault_status = true;
+ sma1307->init_vol = 0x32;
+ sma1307->cur_vol = sma1307->init_vol;
+ sma1307->sw_ot1_prot = true;
+
+ mutex_init(&sma1307->default_lock);
+
+ INIT_DELAYED_WORK(&sma1307->check_fault_work,
+ sma1307_check_fault_worker);
+
+ sma1307->dev = &client->dev;
+ sma1307->kobj = &client->dev.kobj;
+
+ i2c_set_clientdata(client, sma1307);
+
+ sma1307->pll_matches = sma1307_pll_matches;
+
+ regmap_read(sma1307->regmap,
+ SMA1307_FF_DEVICE_INDEX, &device_info);
+
+ if ((device_info & 0xF8) != SMA1307_DEVICE_ID) {
+ dev_err(&client->dev,
+ "%s: device initialization error (0x%02X)",
+ __func__, device_info);
+ return -ENODEV;
+ }
+ dev_dbg(&client->dev, "%s: chip version 0x%02X\n",
+ __func__, device_info);
+
+ i2c_set_clientdata(client, sma1307);
+
+ ret = devm_snd_soc_register_component(&client->dev,
+ &sma1307_component, sma1307_dai,
+ 1);
+
+ if (ret) {
+ dev_err(&client->dev, "%s: failed to register component\n",
+ __func__);
+
+ return ret;
+ }
+
+ return ret;
+}
+
+static void sma1307_i2c_remove(struct i2c_client *client)
+{
+ struct sma1307_priv *sma1307 =
+ (struct sma1307_priv *)i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&sma1307->check_fault_work);
+}
+
+static const struct i2c_device_id sma1307_i2c_id[] = {
+ { "sma1307a" },
+ { "sma1307aq" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, sma1307_i2c_id);
+
+static const struct of_device_id sma1307_of_match[] = {
+ {
+ .compatible = "irondevice,sma1307a",
+ },
+ {
+ .compatible = "irondevice,sma1307aq",
+ .data = &sma1307aq_data //AEC-Q100 Qualificated
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, sma1307_of_match);
+
+static struct i2c_driver sma1307_i2c_driver = {
+ .driver = {
+ .name = "sma1307",
+ .of_match_table = sma1307_of_match,
+ },
+ .probe = sma1307_i2c_probe,
+ .remove = sma1307_i2c_remove,
+ .id_table = sma1307_i2c_id,
+};
+
+module_i2c_driver(sma1307_i2c_driver);
+
+MODULE_DESCRIPTION("ALSA SoC SMA1307 driver");
+MODULE_AUTHOR("Gyuhwa Park, <gyuhwa.park@irondevice.com>");
+MODULE_AUTHOR("KS Jo, <kiseok.jo@irondevice.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sma1307.h b/sound/soc/codecs/sma1307.h
new file mode 100644
index 000000000000..44aab52a32f9
--- /dev/null
+++ b/sound/soc/codecs/sma1307.h
@@ -0,0 +1,444 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * sma1307.h -- sma1307 ALSA SoC Audio driver
+ *
+ * Copyright 2024 Iron Device Corporation
+ *
+ * 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 _SMA1307_H
+#define _SMA1307_H
+
+#include <sound/soc.h>
+
+enum sma1307_fault {
+ SMA1307_FAULT_OT1,
+ SMA1307_FAULT_OT2,
+ SMA1307_FAULT_UVLO,
+ SMA1307_FAULT_OVP_BST,
+ SMA1307_FAULT_OCP_SPK,
+ SMA1307_FAULT_OCP_BST,
+ SMA1307_FAULT_CLK
+};
+
+enum sma1307_mode {
+ SMA1307_MONO_MODE,
+ SMA1307_LEFT_MODE,
+ SMA1307_RIGHT_MODE,
+};
+
+enum sma1307_sdo_mode {
+ SMA1307_OUT_DATA_ONE_48K,
+ SMA1307_OUT_DATA_TWO_48K,
+ SMA1307_OUT_DATA_TWO_24K,
+ SMA1307_OUT_CLK_PLL,
+ SMA1307_OUT_CLK_OSC
+};
+
+enum sma1307_sdo_source {
+ SMA1307_OUT_DISABLE,
+ SMA1307_OUT_FORMAT_C,
+ SMA1307_OUT_MIXER_OUT,
+ SMA1307_OUT_AFTER_DSP,
+ SMA1307_OUT_VRMS2_AVG,
+ SMA1307_OUT_BATTERY,
+ SMA1307_OUT_TEMP,
+ SMA1307_OUT_AFTER_DELAY
+};
+
+struct sma1307_setting_file {
+ bool status;
+ char *header;
+ int *def;
+ int *mode_set[5];
+ int checksum;
+ int num_mode;
+ size_t header_size;
+ size_t def_size;
+ size_t mode_size;
+};
+
+#define SMA1307_I2C_ADDR_00 0x1e
+#define SMA1307_I2C_ADDR_01 0x3e
+#define SMA1307_I2C_ADDR_10 0x5e
+#define SMA1307_I2C_ADDR_11 0x7e
+
+#define DEVICE_NAME_SMA1307A "sma1307a"
+#define DEVICE_NAME_SMA1307AQ "sma1307aq"
+
+#define SMA1307_EXTERNAL_CLOCK_19_2 0x00
+#define SMA1307_EXTERNAL_CLOCK_24_576 0x01
+#define SMA1307_PLL_CLKIN_MCLK 0x02
+#define SMA1307_PLL_CLKIN_BCLK 0x03
+
+#define SMA1307_OFFSET_DEFAULT_MODE 0x00
+#define SMA1307_OFFSET_BURNING_MODE 0x01
+
+#define SMA1307_SETTING_HEADER_SIZE 0x08
+#define SMA1307_SETTING_DEFAULT_SIZE 0xC0
+
+#define SMA1307_DEFAULT_SET 0x00
+#define SMA1307_BINARY_FILE_SET 0x01
+
+/* Controls Name */
+#define SMA1307_REG_CTRL_NAME "Register Byte Control"
+#define SMA1307_VOL_CTRL_NAME "Speaker Volume"
+#define SMA1307_FORCE_MUTE_CTRL_NAME "Force Mute Switch"
+#define SMA1307_TDM_RX0_POS_NAME "TDM RX Slot0 Position"
+#define SMA1307_TDM_RX1_POS_NAME "TDM RX Slot1 Position"
+#define SMA1307_TDM_TX0_POS_NAME "TDM TX Slot0 Position"
+#define SMA1307_TDM_TX1_POS_NAME "TDM TX Slot1 Position"
+#define SMA1307_OT1_SW_PROT_CTRL_NAME "OT1 SW Protection Switch"
+#define SMA1307_RESET_CTRL_NAME "Reset Switch"
+#define SMA1307_CHECK_FAULT_STATUS_NAME "Check Fault Status"
+#define SMA1307_CHECK_FAULT_PERIOD_NAME "Check Fault Period"
+
+/* DAPM Name */
+#define SMA1307_AIF_IN_NAME "AIF IN Source"
+#define SMA1307_AIF_OUT0_NAME "AIF OUT0 Source"
+#define SMA1307_AIF_OUT1_NAME "AIF OUT1 Source"
+
+/*
+ * SMA1307 Register Definition
+ */
+
+/* SMA1307 Register Addresses */
+#define SMA1307_00_SYSTEM_CTRL 0x00
+#define SMA1307_01_INPUT_CTRL1 0x01
+#define SMA1307_02_BROWN_OUT_PROT1 0x02
+#define SMA1307_03_BROWN_OUT_PROT2 0x03
+#define SMA1307_04_BROWN_OUT_PROT3 0x04
+#define SMA1307_05_BROWN_OUT_PROT8 0x05
+#define SMA1307_06_BROWN_OUT_PROT9 0x06
+#define SMA1307_07_BROWN_OUT_PROT10 0x07
+#define SMA1307_08_BROWN_OUT_PROT11 0x08
+#define SMA1307_09_OUTPUT_CTRL 0x09
+#define SMA1307_0A_SPK_VOL 0x0A
+#define SMA1307_0B_BST_TEST 0x0B
+#define SMA1307_0C_BOOST_CTRL8 0x0C
+#define SMA1307_0D_SPK_TEST 0x0D
+#define SMA1307_0E_MUTE_VOL_CTRL 0x0E
+#define SMA1307_0F_VBAT_TEMP_SENSING 0x0F
+
+#define SMA1307_10_SYSTEM_CTRL1 0x10
+#define SMA1307_11_SYSTEM_CTRL2 0x11
+#define SMA1307_12_SYSTEM_CTRL3 0x12
+#define SMA1307_13_DELAY 0x13
+#define SMA1307_14_MODULATOR 0x14
+#define SMA1307_15_BASS_SPK1 0x15
+#define SMA1307_16_BASS_SPK2 0x16
+#define SMA1307_17_BASS_SPK3 0x17
+#define SMA1307_18_BASS_SPK4 0x18
+#define SMA1307_19_BASS_SPK5 0x19
+#define SMA1307_1A_BASS_SPK6 0x1A
+#define SMA1307_1B_BASS_SPK7 0x1B
+#define SMA1307_1C_BROWN_OUT_PROT20 0x1C
+#define SMA1307_1D_BROWN_OUT_PROT0 0x1D
+#define SMA1307_1E_TONE_GENERATOR 0x1E
+#define SMA1307_1F_TONE_FINE_VOLUME 0x1F
+
+#define SMA1307_22_COMP_HYS_SEL 0x22
+#define SMA1307_23_COMPLIM1 0x23
+#define SMA1307_24_COMPLIM2 0x24
+#define SMA1307_25_COMPLIM3 0x25
+#define SMA1307_26_COMPLIM4 0x26
+#define SMA1307_27_BROWN_OUT_PROT4 0x27
+#define SMA1307_28_BROWN_OUT_PROT5 0x28
+#define SMA1307_29_BROWN_OUT_PROT12 0x29
+#define SMA1307_2A_BROWN_OUT_PROT13 0x2A
+#define SMA1307_2B_BROWN_OUT_PROT14 0x2B
+#define SMA1307_2C_BROWN_OUT_PROT15 0x2C
+#define SMA1307_2D_BROWN_OUT_PROT6 0x2D
+#define SMA1307_2E_BROWN_OUT_PROT7 0x2E
+#define SMA1307_2F_BROWN_OUT_PROT16 0x2F
+
+#define SMA1307_30_BROWN_OUT_PROT17 0x30
+#define SMA1307_31_BROWN_OUT_PROT18 0x31
+#define SMA1307_32_BROWN_OUT_PROT19 0x32
+#define SMA1307_34_OCP_SPK 0x34
+#define SMA1307_35_FDPEC_CTRL0 0x35
+#define SMA1307_36_PROTECTION 0x36
+#define SMA1307_37_SLOPECTRL 0x37
+#define SMA1307_38_POWER_METER 0x38
+#define SMA1307_39_PMT_NZ_VAL 0x39
+#define SMA1307_3B_TEST1 0x3B
+#define SMA1307_3C_TEST2 0x3C
+#define SMA1307_3D_TEST3 0x3D
+#define SMA1307_3E_IDLE_MODE_CTRL 0x3E
+#define SMA1307_3F_ATEST2 0x3F
+#define SMA1307_8B_PLL_POST_N 0x8B
+#define SMA1307_8C_PLL_N 0x8C
+#define SMA1307_8D_PLL_A_SETTING 0x8D
+#define SMA1307_8E_PLL_P_CP 0x8E
+#define SMA1307_8F_ANALOG_TEST 0x8F
+
+#define SMA1307_90_CRESTLIM1 0x90
+#define SMA1307_91_CRESTLIM2 0x91
+#define SMA1307_92_FDPEC_CTRL1 0x92
+#define SMA1307_93_INT_CTRL 0x93
+#define SMA1307_94_BOOST_CTRL9 0x94
+#define SMA1307_95_BOOST_CTRL10 0x95
+#define SMA1307_96_BOOST_CTRL11 0x96
+#define SMA1307_97_OTP_TRM0 0x97
+#define SMA1307_98_OTP_TRM1 0x98
+#define SMA1307_99_OTP_TRM2 0x99
+#define SMA1307_9A_OTP_TRM3 0x9A
+
+#define SMA1307_A0_PAD_CTRL0 0xA0
+#define SMA1307_A1_PAD_CTRL1 0xA1
+#define SMA1307_A2_TOP_MAN1 0xA2
+#define SMA1307_A3_TOP_MAN2 0xA3
+#define SMA1307_A4_TOP_MAN3 0xA4
+#define SMA1307_A5_TDM1 0xA5
+#define SMA1307_A6_TDM2 0xA6
+#define SMA1307_A7_CLK_MON 0xA7
+#define SMA1307_A8_BOOST_CTRL1 0xA8
+#define SMA1307_A9_BOOST_CTRL2 0xA9
+#define SMA1307_AA_BOOST_CTRL3 0xAA
+#define SMA1307_AB_BOOST_CTRL4 0xAB
+#define SMA1307_AC_BOOST_CTRL5 0xAC
+#define SMA1307_AD_BOOST_CTRL6 0xAD
+#define SMA1307_AE_BOOST_CTRL7 0xAE
+#define SMA1307_AF_LPF 0xAF
+
+#define SMA1307_B0_RMS_TC1 0xB0
+#define SMA1307_B1_RMS_TC2 0xB1
+#define SMA1307_B2_AVG_TC1 0xB2
+#define SMA1307_B3_AVG_TC2 0xB3
+#define SMA1307_B4_PRVALUE1 0xB4
+#define SMA1307_B5_PRVALUE2 0xB5
+#define SMA1307_B8_SPK_NG_CTRL1 0xB8
+#define SMA1307_B9_SPK_NG_CTRL2 0xB9
+#define SMA1307_BA_DGC1 0xBA
+#define SMA1307_BB_DGC2 0xBB
+#define SMA1307_BC_DGC3 0xBC
+#define SMA1307_BD_MCBS_CTRL1 0xBD
+#define SMA1307_BE_MCBS_CTRL2 0xBE
+
+/* Status Register Read Only */
+#define SMA1307_F5_READY_FOR_V_SAR 0xF5
+#define SMA1307_F7_READY_FOR_T_SAR 0xF7
+#define SMA1307_F8_STATUS_T1 0xF8
+#define SMA1307_F9_STATUS_T2 0xF9
+#define SMA1307_FA_STATUS1 0xFA
+#define SMA1307_FB_STATUS2 0xFB
+#define SMA1307_FC_STATUS3 0xFC
+#define SMA1307_FD_STATUS4 0xFD
+#define SMA1307_FE_STATUS5 0xFE
+#define SMA1307_FF_DEVICE_INDEX 0xFF
+
+/* SMA1307 Registers Bit Fields */
+/* Power On/Off */
+#define SMA1307_POWER_MASK BIT(0)
+#define SMA1307_POWER_OFF 0
+#define SMA1307_POWER_ON BIT(0)
+
+/* Reset */
+#define SMA1307_RESET_MASK BIT(1)
+#define SMA1307_RESET_ON BIT(1)
+
+/* Left Polarity */
+#define SMA1307_LEFTPOL_MASK BIT(3)
+#define SMA1307_LOW_FIRST_CH 0
+#define SMA1307_HIGH_FIRST_CH BIT(3)
+
+/* SCK Falling/Rising */
+#define SMA1307_SCK_RISING_MASK BIT(2)
+#define SMA1307_SCK_FALLING_EDGE 0
+#define SMA1307_SCK_RISING_EDGE BIT(2)
+
+/* SPK Mute */
+#define SMA1307_SPK_MUTE_MASK BIT(0)
+#define SMA1307_SPK_UNMUTE 0
+#define SMA1307_SPK_MUTE BIT(0)
+
+/* SPK Mode */
+#define SMA1307_SPK_MODE_MASK (BIT(2)|BIT(3)|BIT(4))
+#define SMA1307_SPK_OFF 0
+#define SMA1307_SPK_MONO BIT(2)
+#define SMA1307_SPK_STEREO BIT(4)
+
+/* Mono Mix */
+#define SMA1307_MONOMIX_MASK BIT(0)
+#define SMA1307_MONOMIX_OFF 0
+#define SMA1307_MONOMIX_ON BIT(0)
+
+/* LR Data Swap */
+#define SMA1307_LR_DATA_SW_MASK BIT(4)
+#define SMA1307_LR_DATA_SW_NORMAL 0
+#define SMA1307_LR_DATA_SW_SWAP BIT(4)
+
+/* PLL On/Off */
+#define SMA1307_PLL_MASK BIT(6)
+#define SMA1307_PLL_ON 0
+#define SMA1307_PLL_OFF BIT(6)
+
+/* Input Format */
+#define SMA1307_I2S_MODE_MASK (BIT(4)|BIT(5)|BIT(6))
+#define SMA1307_STANDARD_I2S 0
+#define SMA1307_LJ BIT(4)
+#define SMA1307_RJ_16BIT BIT(6)
+#define SMA1307_RJ_18BIT (BIT(4)|BIT(6))
+#define SMA1307_RJ_20BIT (BIT(5)|BIT(6))
+#define SMA1307_RJ_24BIT (BIT(4)|BIT(5)|BIT(6))
+
+/* Controller / Device Setting */
+#define SMA1307_CONTROLLER_DEVICE_MASK BIT(7)
+#define SMA1307_DEVICE_MODE 0
+#define SMA1307_CONTROLLER_MODE BIT(7)
+
+/* Port Config */
+#define SMA1307_PORT_CONFIG_MASK (BIT(6)|BIT(7))
+#define SMA1307_INPUT_PORT_ONLY 0
+#define SMA1307_OUTPUT_PORT_ENABLE BIT(7)
+
+/* SDO Output */
+#define SMA1307_SDO_OUTPUT_MASK BIT(3)
+#define SMA1307_LOGIC_OUTPUT 0
+#define SMA1307_HIGH_Z_OUTPUT BIT(3)
+
+#define SMA1307_DATA_CLK_SEL_MASK (BIT(6)|BIT(7))
+#define SMA1307_SDO_DATA 0
+#define SMA1307_SDO_CLK_PLL BIT(6)
+#define SMA1307_SDO_CLK_OSC (BIT(6)|BIT(7))
+
+/* SDO Output2 */
+#define SMA1307_SDO_OUTPUT2_MASK BIT(0)
+#define SMA1307_ONE_SDO_PER_CH 0
+#define SMA1307_TWO_SDO_PER_CH BIT(0)
+
+/* SDO Output3 */
+#define SMA1307_SDO_OUTPUT3_MASK BIT(2)
+#define SMA1307_SDO_OUTPUT3_DIS 0
+#define SMA1307_TWO_SDO_PER_CH_24K BIT(2)
+
+/* SDO OUT1 Select*/
+#define SMA1307_SDO_OUT1_SEL_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_SDO1_DISABLE 0
+#define SMA1307_SDO1_FORMAT_C BIT(3)
+#define SMA1307_SDO1_MONO_MIX BIT(4)
+#define SMA1307_SDO1_AFTER_DSP (BIT(3)|BIT(4))
+#define SMA1307_SDO1_VRMS2_AVG BIT(5)
+#define SMA1307_SDO1_VBAT_MON (BIT(3)|BIT(5))
+#define SMA1307_SDO1_TEMP_MON (BIT(4)|BIT(5))
+#define SMA1307_SDO1_AFTER_DELAY (BIT(3)|BIT(4)|BIT(5))
+
+/* SDO OUT0 Select*/
+#define SMA1307_SDO_OUT0_SEL_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_SDO0_DISABLE 0
+#define SMA1307_SDO0_FORMAT_C BIT(0)
+#define SMA1307_SDO0_MONO_MIX BIT(1)
+#define SMA1307_SDO0_AFTER_DSP (BIT(0)|BIT(1))
+#define SMA1307_SDO0_VRMS2_AVG BIT(2)
+#define SMA1307_SDO0_VBAT_MON (BIT(0)|BIT(2))
+#define SMA1307_SDO0_TEMP_MON (BIT(1)|BIT(2))
+#define SMA1307_SDO0_AFTER_DELAY (BIT(0)|BIT(1)|BIT(2))
+
+/* INTERRUPT Operation */
+#define SMA1307_SEL_INT_MASK BIT(2)
+#define SMA1307_INT_CLEAR_AUTO 0
+#define SMA1307_INT_CLEAR_MANUAL BIT(2)
+
+/* INTERRUPT CLEAR */
+#define SMA1307_CLR_INT_MASK BIT(1)
+#define SMA1307_INT_READY 0
+#define SMA1307_INT_CLEAR BIT(1)
+
+/* INTERRUPT Disable */
+#define SMA1307_DIS_INT_MASK BIT(0)
+#define SMA1307_NORMAL_INT 0
+#define SMA1307_HIGH_Z_INT BIT(0)
+
+/* Interface Control */
+#define SMA1307_INTERFACE_MASK (BIT(5)|BIT(6)|BIT(7))
+#define SMA1307_LJ_FORMAT BIT(5)
+#define SMA1307_I2S_FORMAT (BIT(5)|BIT(6))
+#define SMA1307_TDM_FORMAT BIT(7)
+
+#define SMA1307_SCK_RATE_MASK (BIT(3)|BIT(4))
+#define SMA1307_SCK_64FS 0
+#define SMA1307_SCK_32FS BIT(4)
+
+#define SMA1307_DATA_WIDTH_MASK (BIT(1)|BIT(2))
+#define SMA1307_DATA_24BIT 0
+#define SMA1307_DATA_16BIT (BIT(1)|BIT(2))
+
+#define SMA1307_TDM_TX_MODE_MASK BIT(6)
+#define SMA1307_TDM_TX_MONO 0
+#define SMA1307_TDM_TX_STEREO BIT(6)
+
+#define SMA1307_TDM_SLOT0_RX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_0 0
+#define SMA1307_TDM_SLOT0_RX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_RX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_RX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_RX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_RX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_RX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_RX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_0 0
+#define SMA1307_TDM_SLOT1_RX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_RX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_RX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_RX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_RX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_RX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* TDM2 FORMAT : 0xA6 */
+#define SMA1307_TDM_DL_MASK BIT(7)
+#define SMA1307_TDM_DL_16 0
+#define SMA1307_TDM_DL_32 BIT(7)
+
+#define SMA1307_TDM_N_SLOT_MASK BIT(6)
+#define SMA1307_TDM_N_SLOT_4 0
+#define SMA1307_TDM_N_SLOT_8 BIT(6)
+
+#define SMA1307_TDM_SLOT0_TX_POS_MASK (BIT(3)|BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_0 0
+#define SMA1307_TDM_SLOT0_TX_POS_1 BIT(3)
+#define SMA1307_TDM_SLOT0_TX_POS_2 BIT(4)
+#define SMA1307_TDM_SLOT0_TX_POS_3 (BIT(3)|BIT(4))
+#define SMA1307_TDM_SLOT0_TX_POS_4 BIT(5)
+#define SMA1307_TDM_SLOT0_TX_POS_5 (BIT(3)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_6 (BIT(4)|BIT(5))
+#define SMA1307_TDM_SLOT0_TX_POS_7 (BIT(3)|BIT(4)|BIT(5))
+
+#define SMA1307_TDM_SLOT1_TX_POS_MASK (BIT(0)|BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_0 0
+#define SMA1307_TDM_SLOT1_TX_POS_1 BIT(0)
+#define SMA1307_TDM_SLOT1_TX_POS_2 BIT(1)
+#define SMA1307_TDM_SLOT1_TX_POS_3 (BIT(0)|BIT(1))
+#define SMA1307_TDM_SLOT1_TX_POS_4 BIT(2)
+#define SMA1307_TDM_SLOT1_TX_POS_5 (BIT(0)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_6 (BIT(1)|BIT(2))
+#define SMA1307_TDM_SLOT1_TX_POS_7 (BIT(0)|BIT(1)|BIT(2))
+
+/* OTP STATUS */
+#define SMA1307_OTP_STAT_MASK BIT(6)
+#define SMA1307_OTP_STAT_0 0
+#define SMA1307_OTP_STAT_1 BIT(6)
+
+/* STATUS */
+#define SMA1307_OT1_OK_STATUS BIT(7)
+#define SMA1307_OT2_OK_STATUS BIT(6)
+#define SMA1307_UVLO_STATUS BIT(5)
+#define SMA1307_OVP_BST_STATUS BIT(4)
+#define SMA1307_POWER_FLAG BIT(3)
+
+#define SMA1307_SCAN_CHK BIT(7)
+#define SMA1307_OCP_SPK_STATUS BIT(5)
+#define SMA1307_OCP_BST_STATUS BIT(4)
+#define SMA1307_BOP_STATE (BIT(1)|BIT(2)|BIT(3))
+#define SMA1307_CLK_MON_STATUS BIT(0)
+
+#define SMA1307_DEVICE_ID (BIT(3)|BIT(4))
+#define SMA1307_REV_NUM_STATUS (BIT(0)|BIT(1))
+#define SMA1307_REV_NUM_REV0 0
+#define SMA1307_REV_NUM_REV1 BIT(0)
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
deleted file mode 100644
index 47b257e41809..000000000000
--- a/sound/soc/codecs/sn95031.c
+++ /dev/null
@@ -1,928 +0,0 @@
-/*
- * sn95031.c - TI sn95031 Codec driver
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
- */
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include <asm/intel_scu_ipc.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/jack.h>
-#include "sn95031.h"
-
-#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
-#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-
-/* adc helper functions */
-
-/* enables mic bias voltage */
-static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
-{
- snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
-}
-
-/* Enable/Disable the ADC depending on the argument */
-static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
-{
- int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
-
- if (val) {
- /* Enable and start the ADC */
- value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
- value &= (~SN95031_ADC_NO_LOOP);
- } else {
- /* Just stop the ADC */
- value &= (~SN95031_ADC_START);
- }
- snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
-}
-
-/*
- * finds an empty channel for conversion
- * If the ADC is not enabled then start using 0th channel
- * itself. Otherwise find an empty channel by looking for a
- * channel in which the stopbit is set to 1. returns the index
- * of the first free channel if succeeds or an error code.
- *
- * Context: can sleep
- *
- */
-static int find_free_channel(struct snd_soc_codec *sn95031_codec)
-{
- int i, value;
-
- /* check whether ADC is enabled */
- value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
-
- if ((value & SN95031_ADC_ENBL) == 0)
- return 0;
-
- /* ADC is already enabled; Looking for an empty channel */
- for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
- value = snd_soc_read(sn95031_codec,
- SN95031_ADC_CHNL_START_ADDR + i);
- if (value & SN95031_STOPBIT_MASK)
- break;
- }
- return (i == SN95031_ADC_CHANLS_MAX) ? (-EINVAL) : i;
-}
-
-/* Initialize the ADC for reading micbias values. Can sleep. */
-static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
-{
- int base_addr, chnl_addr;
- int value;
- int channel_index;
-
- /* Index of the first channel in which the stop bit is set */
- channel_index = find_free_channel(sn95031_codec);
- if (channel_index < 0) {
- pr_err("No free ADC channels");
- return channel_index;
- }
-
- base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
-
- if (!(channel_index == 0 || channel_index == SN95031_ADC_LOOP_MAX)) {
- /* Reset stop bit for channels other than 0 and 12 */
- value = snd_soc_read(sn95031_codec, base_addr);
- /* Set the stop bit to zero */
- snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
- /* Index of the first free channel */
- base_addr++;
- channel_index++;
- }
-
- /* Since this is the last channel, set the stop bit
- to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
- snd_soc_write(sn95031_codec, base_addr,
- SN95031_AUDIO_DETECT_CODE | 0x10);
-
- chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
- pr_debug("mid_initialize : %x", chnl_addr);
- configure_adc(sn95031_codec, 1);
- return chnl_addr;
-}
-
-
-/* reads the ADC registers and gets the mic bias value in mV. */
-static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
-{
- u16 adc_adr = sn95031_initialize_adc(codec);
- u16 adc_val1, adc_val2;
- unsigned int mic_bias;
-
- sn95031_enable_mic_bias(codec);
-
- /* Enable the sound card for conversion before reading */
- snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
- /* Re-toggle the RRDATARD bit */
- snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
-
- /* Read the higher bits of data */
- msleep(1000);
- adc_val1 = snd_soc_read(codec, adc_adr);
- adc_adr++;
- adc_val2 = snd_soc_read(codec, adc_adr);
-
- /* Adding lower two bits to the higher bits */
- mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
- mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
- pr_debug("mic bias = %dmV\n", mic_bias);
- return mic_bias;
-}
-/*end - adc helper functions */
-
-static int sn95031_read(void *ctx, unsigned int reg, unsigned int *val)
-{
- u8 value = 0;
- int ret;
-
- ret = intel_scu_ipc_ioread8(reg, &value);
- if (ret == 0)
- *val = value;
-
- return ret;
-}
-
-static int sn95031_write(void *ctx, unsigned int reg, unsigned int value)
-{
- return intel_scu_ipc_iowrite8(reg, value);
-}
-
-static const struct regmap_config sn95031_regmap = {
- .reg_read = sn95031_read,
- .reg_write = sn95031_write,
-};
-
-static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
- enum snd_soc_bias_level level)
-{
- switch (level) {
- case SND_SOC_BIAS_ON:
- break;
-
- case SND_SOC_BIAS_PREPARE:
- if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
- pr_debug("vaud_bias powering up pll\n");
- /* power up the pll */
- snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
- /* enable pcm 2 */
- snd_soc_update_bits(codec, SN95031_PCM2C2,
- BIT(0), BIT(0));
- }
- break;
-
- case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
- pr_debug("vaud_bias power up rail\n");
- /* power up the rail */
- snd_soc_write(codec, SN95031_VAUD,
- BIT(2)|BIT(1)|BIT(0));
- msleep(1);
- } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
- /* turn off pcm */
- pr_debug("vaud_bias power dn pcm\n");
- snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
- snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
- }
- break;
-
-
- case SND_SOC_BIAS_OFF:
- pr_debug("vaud_bias _OFF doing rail shutdown\n");
- snd_soc_write(codec, SN95031_VAUD, BIT(3));
- break;
- }
-
- codec->dapm.bias_level = level;
- return 0;
-}
-
-static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
- /* power up the rail */
- snd_soc_write(codec, SN95031_VHSP, 0x3D);
- snd_soc_write(codec, SN95031_VHSN, 0x3F);
- msleep(1);
- } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
- pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
- snd_soc_write(codec, SN95031_VHSP, 0xC4);
- snd_soc_write(codec, SN95031_VHSN, 0x04);
- }
- return 0;
-}
-
-static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
- /* power up the rail */
- snd_soc_write(codec, SN95031_VIHF, 0x27);
- msleep(1);
- } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
- pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
- snd_soc_write(codec, SN95031_VIHF, 0x24);
- }
- return 0;
-}
-
-static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ldo = BIT(5)|BIT(4);
- clk_dir = BIT(0);
- data_dir = BIT(7);
- }
- /* program DMIC LDO, clock and set clock */
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
- snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(0), clk_dir);
- snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(7), data_dir);
- return 0;
-}
-
-static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
-
- if (SND_SOC_DAPM_EVENT_ON(event)) {
- ldo = BIT(5)|BIT(4);
- clk_dir = BIT(2);
- data_dir = BIT(1);
- }
- /* program DMIC LDO, clock and set clock */
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
- snd_soc_update_bits(codec, SN95031_DMICBUF0123, BIT(2), clk_dir);
- snd_soc_update_bits(codec, SN95031_DMICBUF45, BIT(1), data_dir);
- return 0;
-}
-
-static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
- unsigned int ldo = 0;
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- ldo = BIT(7)|BIT(6);
-
- /* program DMIC LDO */
- snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo);
- return 0;
-}
-
-/* mux controls */
-static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
-
-static SOC_ENUM_SINGLE_DECL(sn95031_micl_enum,
- SN95031_ADCCONFIG, 1, sn95031_mic_texts);
-
-static const struct snd_kcontrol_new sn95031_micl_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_micl_enum);
-
-static SOC_ENUM_SINGLE_DECL(sn95031_micr_enum,
- SN95031_ADCCONFIG, 3, sn95031_mic_texts);
-
-static const struct snd_kcontrol_new sn95031_micr_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_micr_enum);
-
-static const char *sn95031_input_texts[] = { "DMIC1", "DMIC2", "DMIC3",
- "DMIC4", "DMIC5", "DMIC6",
- "ADC Left", "ADC Right" };
-
-static SOC_ENUM_SINGLE_DECL(sn95031_input1_enum,
- SN95031_AUDIOMUX12, 0, sn95031_input_texts);
-
-static const struct snd_kcontrol_new sn95031_input1_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_input1_enum);
-
-static SOC_ENUM_SINGLE_DECL(sn95031_input2_enum,
- SN95031_AUDIOMUX12, 4, sn95031_input_texts);
-
-static const struct snd_kcontrol_new sn95031_input2_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_input2_enum);
-
-static SOC_ENUM_SINGLE_DECL(sn95031_input3_enum,
- SN95031_AUDIOMUX34, 0, sn95031_input_texts);
-
-static const struct snd_kcontrol_new sn95031_input3_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_input3_enum);
-
-static SOC_ENUM_SINGLE_DECL(sn95031_input4_enum,
- SN95031_AUDIOMUX34, 4, sn95031_input_texts);
-
-static const struct snd_kcontrol_new sn95031_input4_mux_control =
- SOC_DAPM_ENUM("Route", sn95031_input4_enum);
-
-/* capture path controls */
-
-static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"};
-
-/* 0dB to 30dB in 10dB steps */
-static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0);
-
-static SOC_ENUM_SINGLE_DECL(sn95031_micmode1_enum,
- SN95031_MICAMP1, 1, sn95031_micmode_text);
-static SOC_ENUM_SINGLE_DECL(sn95031_micmode2_enum,
- SN95031_MICAMP2, 1, sn95031_micmode_text);
-
-static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"};
-
-static SOC_ENUM_SINGLE_DECL(sn95031_dmic12_cfg_enum,
- SN95031_DMICMUX, 0, sn95031_dmic_cfg_text);
-static SOC_ENUM_SINGLE_DECL(sn95031_dmic34_cfg_enum,
- SN95031_DMICMUX, 1, sn95031_dmic_cfg_text);
-static SOC_ENUM_SINGLE_DECL(sn95031_dmic56_cfg_enum,
- SN95031_DMICMUX, 2, sn95031_dmic_cfg_text);
-
-static const struct snd_kcontrol_new sn95031_snd_controls[] = {
- SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
- SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum),
- SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum),
- SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum),
- SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum),
- SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1,
- 2, 4, 0, mic_tlv),
- SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2,
- 2, 4, 0, mic_tlv),
-};
-
-/* DAPM widgets */
-static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
-
- /* all end points mic, hs etc */
- SND_SOC_DAPM_OUTPUT("HPOUTL"),
- SND_SOC_DAPM_OUTPUT("HPOUTR"),
- SND_SOC_DAPM_OUTPUT("EPOUT"),
- SND_SOC_DAPM_OUTPUT("IHFOUTL"),
- SND_SOC_DAPM_OUTPUT("IHFOUTR"),
- SND_SOC_DAPM_OUTPUT("LINEOUTL"),
- SND_SOC_DAPM_OUTPUT("LINEOUTR"),
- SND_SOC_DAPM_OUTPUT("VIB1OUT"),
- SND_SOC_DAPM_OUTPUT("VIB2OUT"),
-
- SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */
- SND_SOC_DAPM_INPUT("AMIC2"),
- SND_SOC_DAPM_INPUT("DMIC1"),
- SND_SOC_DAPM_INPUT("DMIC2"),
- SND_SOC_DAPM_INPUT("DMIC3"),
- SND_SOC_DAPM_INPUT("DMIC4"),
- SND_SOC_DAPM_INPUT("DMIC5"),
- SND_SOC_DAPM_INPUT("DMIC6"),
- SND_SOC_DAPM_INPUT("LINEINL"),
- SND_SOC_DAPM_INPUT("LINEINR"),
-
- SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0),
- SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0),
- SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0),
- SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0),
- SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0),
-
- SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0,
- sn95031_dmic12_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0,
- sn95031_dmic34_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0,
- sn95031_dmic56_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
- SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0,
- SND_SOC_NOPM, 0, 0),
-
- SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
- sn95031_vhs_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
- sn95031_vihf_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
-
- /* playback path driver enables */
- SND_SOC_DAPM_PGA("Headset Left Playback",
- SN95031_DRIVEREN, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Headset Right Playback",
- SN95031_DRIVEREN, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Speaker Left Playback",
- SN95031_DRIVEREN, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Speaker Right Playback",
- SN95031_DRIVEREN, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Vibra1 Playback",
- SN95031_DRIVEREN, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Vibra2 Playback",
- SN95031_DRIVEREN, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Earpiece Playback",
- SN95031_DRIVEREN, 6, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Lineout Left Playback",
- SN95031_LOCTL, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Lineout Right Playback",
- SN95031_LOCTL, 4, 0, NULL, 0),
-
- /* playback path filter enable */
- SND_SOC_DAPM_PGA("Headset Left Filter",
- SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Headset Right Filter",
- SN95031_HSEPRXCTRL, 5, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Speaker Left Filter",
- SN95031_IHFRXCTRL, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Speaker Right Filter",
- SN95031_IHFRXCTRL, 1, 0, NULL, 0),
-
- /* DACs */
- SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
- SN95031_DACCONFIG, 0, 0),
- SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
- SN95031_DACCONFIG, 1, 0),
- SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
- SN95031_DACCONFIG, 2, 0),
- SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
- SN95031_DACCONFIG, 3, 0),
- SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
- SN95031_VIB1C5, 1, 0),
- SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
- SN95031_VIB2C5, 1, 0),
-
- /* capture widgets */
- SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1,
- 7, 0, NULL, 0),
- SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2,
- 7, 0, NULL, 0),
-
- SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0),
- SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0),
- SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0),
-
- /* ADC have null stream as they will be turned ON by TX path */
- SND_SOC_DAPM_ADC("ADC Left", NULL,
- SN95031_ADCCONFIG, 0, 0),
- SND_SOC_DAPM_ADC("ADC Right", NULL,
- SN95031_ADCCONFIG, 2, 0),
-
- SND_SOC_DAPM_MUX("Mic_InputL Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control),
- SND_SOC_DAPM_MUX("Mic_InputR Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control),
-
- SND_SOC_DAPM_MUX("Txpath1 Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control),
- SND_SOC_DAPM_MUX("Txpath2 Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control),
- SND_SOC_DAPM_MUX("Txpath3 Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control),
- SND_SOC_DAPM_MUX("Txpath4 Capture Route",
- SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control),
-
-};
-
-static const struct snd_soc_dapm_route sn95031_audio_map[] = {
- /* headset and earpiece map */
- { "HPOUTL", NULL, "Headset Rail"},
- { "HPOUTR", NULL, "Headset Rail"},
- { "HPOUTL", NULL, "Headset Left Playback" },
- { "HPOUTR", NULL, "Headset Right Playback" },
- { "EPOUT", NULL, "Earpiece Playback" },
- { "Headset Left Playback", NULL, "Headset Left Filter"},
- { "Headset Right Playback", NULL, "Headset Right Filter"},
- { "Earpiece Playback", NULL, "Headset Left Filter"},
- { "Headset Left Filter", NULL, "HSDAC Left"},
- { "Headset Right Filter", NULL, "HSDAC Right"},
-
- /* speaker map */
- { "IHFOUTL", NULL, "Speaker Rail"},
- { "IHFOUTR", NULL, "Speaker Rail"},
- { "IHFOUTL", "NULL", "Speaker Left Playback"},
- { "IHFOUTR", "NULL", "Speaker Right Playback"},
- { "Speaker Left Playback", NULL, "Speaker Left Filter"},
- { "Speaker Right Playback", NULL, "Speaker Right Filter"},
- { "Speaker Left Filter", NULL, "IHFDAC Left"},
- { "Speaker Right Filter", NULL, "IHFDAC Right"},
-
- /* vibra map */
- { "VIB1OUT", NULL, "Vibra1 Playback"},
- { "Vibra1 Playback", NULL, "Vibra1 DAC"},
-
- { "VIB2OUT", NULL, "Vibra2 Playback"},
- { "Vibra2 Playback", NULL, "Vibra2 DAC"},
-
- /* lineout */
- { "LINEOUTL", NULL, "Lineout Left Playback"},
- { "LINEOUTR", NULL, "Lineout Right Playback"},
- { "Lineout Left Playback", NULL, "Headset Left Filter"},
- { "Lineout Left Playback", NULL, "Speaker Left Filter"},
- { "Lineout Left Playback", NULL, "Vibra1 DAC"},
- { "Lineout Right Playback", NULL, "Headset Right Filter"},
- { "Lineout Right Playback", NULL, "Speaker Right Filter"},
- { "Lineout Right Playback", NULL, "Vibra2 DAC"},
-
- /* Headset (AMIC1) mic */
- { "AMIC1Bias", NULL, "AMIC1"},
- { "MIC1 Enable", NULL, "AMIC1Bias"},
- { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"},
-
- /* AMIC2 */
- { "AMIC2Bias", NULL, "AMIC2"},
- { "MIC2 Enable", NULL, "AMIC2Bias"},
- { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"},
-
-
- /* Linein */
- { "LineIn Enable Left", NULL, "LINEINL"},
- { "LineIn Enable Right", NULL, "LINEINR"},
- { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"},
- { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"},
-
- /* ADC connection */
- { "ADC Left", NULL, "Mic_InputL Capture Route"},
- { "ADC Right", NULL, "Mic_InputR Capture Route"},
-
- /*DMIC connections */
- { "DMIC1", NULL, "DMIC12supply"},
- { "DMIC2", NULL, "DMIC12supply"},
- { "DMIC3", NULL, "DMIC34supply"},
- { "DMIC4", NULL, "DMIC34supply"},
- { "DMIC5", NULL, "DMIC56supply"},
- { "DMIC6", NULL, "DMIC56supply"},
-
- { "DMIC12Bias", NULL, "DMIC1"},
- { "DMIC12Bias", NULL, "DMIC2"},
- { "DMIC34Bias", NULL, "DMIC3"},
- { "DMIC34Bias", NULL, "DMIC4"},
- { "DMIC56Bias", NULL, "DMIC5"},
- { "DMIC56Bias", NULL, "DMIC6"},
-
- /*TX path inputs*/
- { "Txpath1 Capture Route", "ADC Left", "ADC Left"},
- { "Txpath2 Capture Route", "ADC Left", "ADC Left"},
- { "Txpath3 Capture Route", "ADC Left", "ADC Left"},
- { "Txpath4 Capture Route", "ADC Left", "ADC Left"},
- { "Txpath1 Capture Route", "ADC Right", "ADC Right"},
- { "Txpath2 Capture Route", "ADC Right", "ADC Right"},
- { "Txpath3 Capture Route", "ADC Right", "ADC Right"},
- { "Txpath4 Capture Route", "ADC Right", "ADC Right"},
- { "Txpath1 Capture Route", "DMIC1", "DMIC1"},
- { "Txpath2 Capture Route", "DMIC1", "DMIC1"},
- { "Txpath3 Capture Route", "DMIC1", "DMIC1"},
- { "Txpath4 Capture Route", "DMIC1", "DMIC1"},
- { "Txpath1 Capture Route", "DMIC2", "DMIC2"},
- { "Txpath2 Capture Route", "DMIC2", "DMIC2"},
- { "Txpath3 Capture Route", "DMIC2", "DMIC2"},
- { "Txpath4 Capture Route", "DMIC2", "DMIC2"},
- { "Txpath1 Capture Route", "DMIC3", "DMIC3"},
- { "Txpath2 Capture Route", "DMIC3", "DMIC3"},
- { "Txpath3 Capture Route", "DMIC3", "DMIC3"},
- { "Txpath4 Capture Route", "DMIC3", "DMIC3"},
- { "Txpath1 Capture Route", "DMIC4", "DMIC4"},
- { "Txpath2 Capture Route", "DMIC4", "DMIC4"},
- { "Txpath3 Capture Route", "DMIC4", "DMIC4"},
- { "Txpath4 Capture Route", "DMIC4", "DMIC4"},
- { "Txpath1 Capture Route", "DMIC5", "DMIC5"},
- { "Txpath2 Capture Route", "DMIC5", "DMIC5"},
- { "Txpath3 Capture Route", "DMIC5", "DMIC5"},
- { "Txpath4 Capture Route", "DMIC5", "DMIC5"},
- { "Txpath1 Capture Route", "DMIC6", "DMIC6"},
- { "Txpath2 Capture Route", "DMIC6", "DMIC6"},
- { "Txpath3 Capture Route", "DMIC6", "DMIC6"},
- { "Txpath4 Capture Route", "DMIC6", "DMIC6"},
-
- /* tx path */
- { "TX1 Enable", NULL, "Txpath1 Capture Route"},
- { "TX2 Enable", NULL, "Txpath2 Capture Route"},
- { "TX3 Enable", NULL, "Txpath3 Capture Route"},
- { "TX4 Enable", NULL, "Txpath4 Capture Route"},
- { "PCM_Out", NULL, "TX1 Enable"},
- { "PCM_Out", NULL, "TX2 Enable"},
- { "PCM_Out", NULL, "TX3 Enable"},
- { "PCM_Out", NULL, "TX4 Enable"},
-
-};
-
-/* speaker and headset mutes, for audio pops and clicks */
-static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
-{
- snd_soc_update_bits(dai->codec,
- SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
- snd_soc_update_bits(dai->codec,
- SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
- return 0;
-}
-
-static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
-{
- snd_soc_update_bits(dai->codec,
- SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
- snd_soc_update_bits(dai->codec,
- SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
- return 0;
-}
-
-static int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
-{
- unsigned int format, rate;
-
- switch (params_width(params)) {
- case 16:
- format = BIT(4)|BIT(5);
- break;
-
- case 24:
- format = 0;
- break;
- default:
- return -EINVAL;
- }
- snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
- BIT(4)|BIT(5), format);
-
- switch (params_rate(params)) {
- case 48000:
- pr_debug("RATE_48000\n");
- rate = 0;
- break;
-
- case 44100:
- pr_debug("RATE_44100\n");
- rate = BIT(7);
- break;
-
- default:
- pr_err("ERR rate %d\n", params_rate(params));
- return -EINVAL;
- }
- snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
-
- return 0;
-}
-
-/* Codec DAI section */
-static const struct snd_soc_dai_ops sn95031_headset_dai_ops = {
- .digital_mute = sn95031_pcm_hs_mute,
- .hw_params = sn95031_pcm_hw_params,
-};
-
-static const struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
- .digital_mute = sn95031_pcm_spkr_mute,
- .hw_params = sn95031_pcm_hw_params,
-};
-
-static const struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
- .hw_params = sn95031_pcm_hw_params,
-};
-
-static const struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
- .hw_params = sn95031_pcm_hw_params,
-};
-
-static struct snd_soc_dai_driver sn95031_dais[] = {
-{
- .name = "SN95031 Headset",
- .playback = {
- .stream_name = "Headset",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SN95031_RATES,
- .formats = SN95031_FORMATS,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 5,
- .rates = SN95031_RATES,
- .formats = SN95031_FORMATS,
- },
- .ops = &sn95031_headset_dai_ops,
-},
-{ .name = "SN95031 Speaker",
- .playback = {
- .stream_name = "Speaker",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SN95031_RATES,
- .formats = SN95031_FORMATS,
- },
- .ops = &sn95031_speaker_dai_ops,
-},
-{ .name = "SN95031 Vibra1",
- .playback = {
- .stream_name = "Vibra1",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SN95031_RATES,
- .formats = SN95031_FORMATS,
- },
- .ops = &sn95031_vib1_dai_ops,
-},
-{ .name = "SN95031 Vibra2",
- .playback = {
- .stream_name = "Vibra2",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SN95031_RATES,
- .formats = SN95031_FORMATS,
- },
- .ops = &sn95031_vib2_dai_ops,
-},
-};
-
-static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec)
-{
- snd_soc_write(codec, SN95031_BTNCTRL2, 0x00);
-}
-
-static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
-{
- snd_soc_write(codec, SN95031_BTNCTRL1, 0x77);
- snd_soc_write(codec, SN95031_BTNCTRL2, 0x01);
-}
-
-static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
-{
- int micbias = sn95031_get_mic_bias(mfld_jack->codec);
-
- int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
-
- pr_debug("jack type detected = %d\n", jack_type);
- if (jack_type == SND_JACK_HEADSET)
- sn95031_enable_jack_btn(mfld_jack->codec);
- return jack_type;
-}
-
-void sn95031_jack_detection(struct mfld_jack_data *jack_data)
-{
- unsigned int status;
- unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
-
- pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
- if (jack_data->intr_id & 0x1) {
- pr_debug("short_push detected\n");
- status = SND_JACK_HEADSET | SND_JACK_BTN_0;
- } else if (jack_data->intr_id & 0x2) {
- pr_debug("long_push detected\n");
- status = SND_JACK_HEADSET | SND_JACK_BTN_1;
- } else if (jack_data->intr_id & 0x4) {
- pr_debug("headset or headphones inserted\n");
- status = sn95031_get_headset_state(jack_data->mfld_jack);
- } else if (jack_data->intr_id & 0x8) {
- pr_debug("headset or headphones removed\n");
- status = 0;
- sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
- } else {
- pr_err("unidentified interrupt\n");
- return;
- }
-
- snd_soc_jack_report(jack_data->mfld_jack, status, mask);
- /*button pressed and released so we send explicit button release */
- if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1))
- snd_soc_jack_report(jack_data->mfld_jack,
- SND_JACK_HEADSET, mask);
-}
-EXPORT_SYMBOL_GPL(sn95031_jack_detection);
-
-/* codec registration */
-static int sn95031_codec_probe(struct snd_soc_codec *codec)
-{
- pr_debug("codec_probe called\n");
-
- /* PCM interface config
- * This sets the pcm rx slot conguration to max 6 slots
- * for max 4 dais (2 stereo and 2 mono)
- */
- snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
- snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
- snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
- snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10);
- snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32);
- /* pcm port setting
- * This sets the pcm port to slave and clock at 19.2Mhz which
- * can support 6slots, sampling rate set per stream in hw-params
- */
- snd_soc_write(codec, SN95031_PCM1C1, 0x00);
- snd_soc_write(codec, SN95031_PCM2C1, 0x01);
- snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
- snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
- /* vendor vibra workround, the vibras are muted by
- * custom register so unmute them
- */
- snd_soc_write(codec, SN95031_SSR5, 0x80);
- snd_soc_write(codec, SN95031_SSR6, 0x80);
- snd_soc_write(codec, SN95031_VIB1C5, 0x00);
- snd_soc_write(codec, SN95031_VIB2C5, 0x00);
- /* configure vibras for pcm port */
- snd_soc_write(codec, SN95031_VIB1C3, 0x00);
- snd_soc_write(codec, SN95031_VIB2C3, 0x00);
-
- /* soft mute ramp time */
- snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
- /* fix the initial volume at 1dB,
- * default in +9dB,
- * 1dB give optimal swing on DAC, amps
- */
- snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
- snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
- snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
- snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
- /* dac mode and lineout workaround */
- snd_soc_write(codec, SN95031_SSR2, 0x10);
- snd_soc_write(codec, SN95031_SSR3, 0x40);
-
- return 0;
-}
-
-static struct snd_soc_codec_driver sn95031_codec = {
- .probe = sn95031_codec_probe,
- .set_bias_level = sn95031_set_vaud_bias,
- .idle_bias_off = true,
-
- .controls = sn95031_snd_controls,
- .num_controls = ARRAY_SIZE(sn95031_snd_controls),
- .dapm_widgets = sn95031_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sn95031_dapm_widgets),
- .dapm_routes = sn95031_audio_map,
- .num_dapm_routes = ARRAY_SIZE(sn95031_audio_map),
-};
-
-static int sn95031_device_probe(struct platform_device *pdev)
-{
- struct regmap *regmap;
-
- pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
-
- regmap = devm_regmap_init(&pdev->dev, NULL, NULL, &sn95031_regmap);
- if (IS_ERR(regmap))
- return PTR_ERR(regmap);
-
- return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
- sn95031_dais, ARRAY_SIZE(sn95031_dais));
-}
-
-static int sn95031_device_remove(struct platform_device *pdev)
-{
- pr_debug("codec device remove called\n");
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver sn95031_codec_driver = {
- .driver = {
- .name = "sn95031",
- },
- .probe = sn95031_device_probe,
- .remove = sn95031_device_remove,
-};
-
-module_platform_driver(sn95031_codec_driver);
-
-MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
-MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
-MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:sn95031");
diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
deleted file mode 100644
index 20376d234fb8..000000000000
--- a/sound/soc/codecs/sn95031.h
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * sn95031.h - TI sn95031 Codec driver
- *
- * Copyright (C) 2010 Intel Corp
- * Author: Vinod Koul <vinod.koul@intel.com>
- * Author: Harsha Priya <priya.harsha@intel.com>
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *
- */
-#ifndef _SN95031_H
-#define _SN95031_H
-
-/*register map*/
-#define SN95031_VAUD 0xDB
-#define SN95031_VHSP 0xDC
-#define SN95031_VHSN 0xDD
-#define SN95031_VIHF 0xC9
-
-#define SN95031_AUDPLLCTRL 0x240
-#define SN95031_DMICBUF0123 0x241
-#define SN95031_DMICBUF45 0x242
-#define SN95031_DMICGPO 0x244
-#define SN95031_DMICMUX 0x245
-#define SN95031_DMICLK 0x246
-#define SN95031_MICBIAS 0x247
-#define SN95031_ADCCONFIG 0x248
-#define SN95031_MICAMP1 0x249
-#define SN95031_MICAMP2 0x24A
-#define SN95031_NOISEMUX 0x24B
-#define SN95031_AUDIOMUX12 0x24C
-#define SN95031_AUDIOMUX34 0x24D
-#define SN95031_AUDIOSINC 0x24E
-#define SN95031_AUDIOTXEN 0x24F
-#define SN95031_HSEPRXCTRL 0x250
-#define SN95031_IHFRXCTRL 0x251
-#define SN95031_HSMIXER 0x256
-#define SN95031_DACCONFIG 0x257
-#define SN95031_SOFTMUTE 0x258
-#define SN95031_HSLVOLCTRL 0x259
-#define SN95031_HSRVOLCTRL 0x25A
-#define SN95031_IHFLVOLCTRL 0x25B
-#define SN95031_IHFRVOLCTRL 0x25C
-#define SN95031_DRIVEREN 0x25D
-#define SN95031_LOCTL 0x25E
-#define SN95031_VIB1C1 0x25F
-#define SN95031_VIB1C2 0x260
-#define SN95031_VIB1C3 0x261
-#define SN95031_VIB1SPIPCM1 0x262
-#define SN95031_VIB1SPIPCM2 0x263
-#define SN95031_VIB1C5 0x264
-#define SN95031_VIB2C1 0x265
-#define SN95031_VIB2C2 0x266
-#define SN95031_VIB2C3 0x267
-#define SN95031_VIB2SPIPCM1 0x268
-#define SN95031_VIB2SPIPCM2 0x269
-#define SN95031_VIB2C5 0x26A
-#define SN95031_BTNCTRL1 0x26B
-#define SN95031_BTNCTRL2 0x26C
-#define SN95031_PCM1TXSLOT01 0x26D
-#define SN95031_PCM1TXSLOT23 0x26E
-#define SN95031_PCM1TXSLOT45 0x26F
-#define SN95031_PCM1RXSLOT0_3 0x270
-#define SN95031_PCM1RXSLOT45 0x271
-#define SN95031_PCM2TXSLOT01 0x272
-#define SN95031_PCM2TXSLOT23 0x273
-#define SN95031_PCM2TXSLOT45 0x274
-#define SN95031_PCM2RXSLOT01 0x275
-#define SN95031_PCM2RXSLOT23 0x276
-#define SN95031_PCM2RXSLOT45 0x277
-#define SN95031_PCM1C1 0x278
-#define SN95031_PCM1C2 0x279
-#define SN95031_PCM1C3 0x27A
-#define SN95031_PCM2C1 0x27B
-#define SN95031_PCM2C2 0x27C
-/*end codec register defn*/
-
-/*vendor defn these are not part of avp*/
-#define SN95031_SSR2 0x381
-#define SN95031_SSR3 0x382
-#define SN95031_SSR5 0x384
-#define SN95031_SSR6 0x385
-
-/* ADC registers */
-
-#define SN95031_ADC1CNTL1 0x1C0
-#define SN95031_ADC_ENBL 0x10
-#define SN95031_ADC_START 0x08
-#define SN95031_ADC1CNTL3 0x1C2
-#define SN95031_ADCTHERM_ENBL 0x04
-#define SN95031_ADCRRDATA_ENBL 0x05
-#define SN95031_STOPBIT_MASK 16
-#define SN95031_ADCTHERM_MASK 4
-#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
-#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
-#define SN95031_ADC_NO_LOOP 0x07
-#define SN95031_AUDIO_GPIO_CTRL 0x070
-
-/* ADC channel code values */
-#define SN95031_AUDIO_DETECT_CODE 0x06
-
-/* ADC base addresses */
-#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
-#define SN95031_ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
-/* multipier to convert to mV */
-#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
-
-
-struct mfld_jack_data {
- int intr_id;
- int micbias_vol;
- struct snd_soc_jack *mfld_jack;
-};
-
-extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
-
-#endif
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index 3ec41ccbf4e2..c9766979b1d7 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIR (Digital Interface Reciever) driver
*
@@ -9,10 +10,6 @@
*
* Author: Vipin Kumar, <vipin.kumar@st.com>
* Copyright: (C) 2012 ST Microelectronics
- *
- * 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.
*/
#include <linux/module.h>
@@ -31,17 +28,22 @@ static const struct snd_soc_dapm_route dir_routes[] = {
{ "Capture", NULL, "spdif-in" },
};
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
-static struct snd_soc_codec_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),
+static const 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 = {
@@ -57,16 +59,11 @@ static struct snd_soc_dai_driver dir_stub_dai = {
static int spdif_dir_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dir,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dir,
&dir_stub_dai, 1);
}
-static int spdif_dir_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dir_dt_ids[] = {
{ .compatible = "linux,spdif-dir", },
@@ -77,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dir_dt_ids);
static struct platform_driver spdif_dir_driver = {
.probe = spdif_dir_probe,
- .remove = spdif_dir_remove,
.driver = {
.name = "spdif-dir",
.of_match_table = of_match_ptr(spdif_dir_dt_ids),
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index ef634a9ad673..2409fd834f84 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ALSA SoC SPDIF DIT driver
*
@@ -8,10 +9,6 @@
* Author: Steve Chen, <schen@mvista.com>
* Copyright: (C) 2009 MontaVista Software, Inc., <source@mvista.com>
* Copyright: (C) 2009 Texas Instruments, India
- *
- * 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.
*/
#include <linux/module.h>
@@ -24,10 +21,12 @@
#define DRV_NAME "spdif-dit"
-#define STUB_RATES SNDRV_PCM_RATE_8000_192000
+#define STUB_RATES (SNDRV_PCM_RATE_8000_768000 | \
+ SNDRV_PCM_RATE_128000)
#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE)
+ 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"),
@@ -37,11 +36,14 @@ static const struct snd_soc_dapm_route dit_routes[] = {
{ "spdif-out", NULL, "Playback" },
};
-static struct snd_soc_codec_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),
+static const 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 = {
@@ -57,16 +59,11 @@ static struct snd_soc_dai_driver dit_stub_dai = {
static int spdif_dit_probe(struct platform_device *pdev)
{
- return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dit,
+ return devm_snd_soc_register_component(&pdev->dev,
+ &soc_codec_spdif_dit,
&dit_stub_dai, 1);
}
-static int spdif_dit_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
-}
-
#ifdef CONFIG_OF
static const struct of_device_id spdif_dit_dt_ids[] = {
{ .compatible = "linux,spdif-dit", },
@@ -77,7 +74,6 @@ MODULE_DEVICE_TABLE(of, spdif_dit_dt_ids);
static struct platform_driver spdif_dit_driver = {
.probe = spdif_dit_probe,
- .remove = spdif_dit_remove,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(spdif_dit_dt_ids),
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
new file mode 100644
index 000000000000..55f00ce7c718
--- /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 <flatmax@flatmax.com>
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#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" },
+ { }
+};
+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 <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c
new file mode 100644
index 000000000000..5a3489475225
--- /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 <flatmax@flatmax.com>
+
+#include <linux/module.h>
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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_CBP_CFP:
+ ctrl = SRC4XXX_BUS_MASTER;
+ src4xxx->master[dai->id] = true;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ 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<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ /* set the PLL for the digital receiver */
+ switch (src4xxx->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<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Couldn't set the TX's div register to %d << %d = 0x%x\n",
+ val, SRC4XXX_TX_MCLK_DIV_SHIFT,
+ val<<SRC4XXX_TX_MCLK_DIV_SHIFT);
+ return ret;
+ }
+
+ return regmap_update_bits(src4xxx->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 <flatmax@flatmax.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/src4xxx.h b/sound/soc/codecs/src4xxx.h
new file mode 100644
index 000000000000..5bf778fb9945
--- /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 <flatmax@flatmax.com>
+
+#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 000000000000..1d022643c307
--- /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 <kernel@pengutronix.de>
+//
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#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 <m.felsch@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 67ea55adb307..06016e88dd27 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -1,20 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
+#include <linux/err.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <linux/platform_data/ssm2518.h>
+#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -115,7 +113,7 @@ struct ssm2518 {
unsigned int sysclk;
const struct snd_pcm_hw_constraint_list *constraints;
- int enable_gpio;
+ struct gpio_desc *enable_gpio;
};
static const struct reg_default ssm2518_reg_defaults[] = {
@@ -309,7 +307,7 @@ static const struct snd_pcm_hw_constraint_list ssm2518_constraints_12288000 = {
.count = ARRAY_SIZE(ssm2518_rates_12288000),
};
-static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
+static int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
unsigned int rate)
{
const unsigned int *sysclks = NULL;
@@ -336,8 +334,8 @@ static unsigned int ssm2518_lookup_mcs(struct ssm2518 *ssm2518,
static int ssm2518_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -389,9 +387,9 @@ static int ssm2518_hw_params(struct snd_pcm_substream *substream,
SSM2518_POWER1_MCS_MASK, mcs << 1);
}
-static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2518_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
if (mute)
@@ -405,13 +403,13 @@ static int ssm2518_mute(struct snd_soc_dai *dai, int mute)
static int ssm2518_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -484,8 +482,8 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
regcache_mark_dirty(ssm2518->regmap);
}
- if (gpio_is_valid(ssm2518->enable_gpio))
- gpio_set_value(ssm2518->enable_gpio, enable);
+ if (ssm2518->enable_gpio)
+ gpiod_set_value_cansleep(ssm2518->enable_gpio, enable);
regcache_cache_only(ssm2518->regmap, !enable);
@@ -498,10 +496,10 @@ static int ssm2518_set_power(struct ssm2518 *ssm2518, bool enable)
return ret;
}
-static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2518_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@@ -510,7 +508,7 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = ssm2518_set_power(ssm2518, true);
break;
case SND_SOC_BIAS_OFF:
@@ -518,18 +516,13 @@ static int ssm2518_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ 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_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
unsigned int ctrl1, ctrl2;
int left_slot, right_slot;
int ret;
@@ -614,7 +607,7 @@ static int ssm2518_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
static int ssm2518_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct ssm2518 *ssm2518 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(dai->component);
if (ssm2518->constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
@@ -629,9 +622,10 @@ static int ssm2518_startup(struct snd_pcm_substream *substream,
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
- .digital_mute = ssm2518_mute,
+ .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 = {
@@ -646,10 +640,10 @@ static struct snd_soc_dai_driver ssm2518_dai = {
.ops = &ssm2518_dai_ops,
};
-static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+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_codec_get_drvdata(codec);
+ struct ssm2518 *ssm2518 = snd_soc_component_get_drvdata(component);
unsigned int val;
if (clk_id != SSM2518_SYSCLK)
@@ -715,40 +709,32 @@ static int ssm2518_set_sysclk(struct snd_soc_codec *codec, int clk_id,
SSM2518_POWER1_NO_BCLK, val);
}
-static struct snd_soc_codec_driver ssm2518_codec_driver = {
- .set_bias_level = ssm2518_set_bias_level,
- .set_sysclk = ssm2518_set_sysclk,
- .idle_bias_off = true,
-
- .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),
+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 bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
-{
- return false;
-}
-
static const struct regmap_config ssm2518_regmap_config = {
.val_bits = 8,
.reg_bits = 8,
.max_register = SSM2518_REG_DRC_9,
- .volatile_reg = ssm2518_register_volatile,
.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,
- const struct i2c_device_id *id)
+static int ssm2518_i2c_probe(struct i2c_client *i2c)
{
- struct ssm2518_platform_data *pdata = i2c->dev.platform_data;
struct ssm2518 *ssm2518;
int ret;
@@ -756,22 +742,14 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ssm2518 == NULL)
return -ENOMEM;
- if (pdata) {
- ssm2518->enable_gpio = pdata->enable_gpio;
- } else if (i2c->dev.of_node) {
- ssm2518->enable_gpio = of_get_gpio(i2c->dev.of_node, 0);
- if (ssm2518->enable_gpio < 0 && ssm2518->enable_gpio != -ENOENT)
- return ssm2518->enable_gpio;
- } else {
- ssm2518->enable_gpio = -1;
- }
+ /* 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;
- if (gpio_is_valid(ssm2518->enable_gpio)) {
- ret = devm_gpio_request_one(&i2c->dev, ssm2518->enable_gpio,
- GPIOF_OUT_INIT_HIGH, "SSM2518 nSD");
- if (ret)
- return ret;
- }
+ gpiod_set_consumer_name(ssm2518->enable_gpio, "SSM2518 nSD");
i2c_set_clientdata(i2c, ssm2518);
@@ -801,18 +779,21 @@ static int ssm2518_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &ssm2518_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev,
+ &ssm2518_component_driver,
&ssm2518_dai, 1);
}
-static int ssm2518_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+#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 },
+ { "ssm2518" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
@@ -820,10 +801,9 @@ MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
static struct i2c_driver ssm2518_driver = {
.driver = {
.name = "ssm2518",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ssm2518_dt_ids),
},
.probe = ssm2518_i2c_probe,
- .remove = ssm2518_i2c_remove,
.id_table = ssm2518_i2c_ids,
};
module_i2c_driver(ssm2518_driver);
diff --git a/sound/soc/codecs/ssm2518.h b/sound/soc/codecs/ssm2518.h
index 62511d80518e..273fd0977470 100644
--- a/sound/soc/codecs/ssm2518.h
+++ b/sound/soc/codecs/ssm2518.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* SSM2518 amplifier audio driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __SND_SOC_CODECS_SSM2518_H__
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
index 0d9779d6bfda..49c74cba17c7 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602/SSM2603/SSM2604 I2C audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,19 +19,12 @@
* low = 0x1a
* high = 0x1b
*/
-static int ssm2602_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int ssm2602_i2c_probe(struct i2c_client *client)
{
- return ssm2602_probe(&client->dev, id->driver_data,
+ return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client),
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
}
-static int ssm2602_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ssm2602_i2c_id[] = {
{ "ssm2602", SSM2602 },
{ "ssm2603", SSM2602 },
@@ -52,11 +44,9 @@ MODULE_DEVICE_TABLE(of, ssm2602_of_match);
static struct i2c_driver ssm2602_i2c_driver = {
.driver = {
.name = "ssm2602",
- .owner = THIS_MODULE,
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_i2c_probe,
- .remove = ssm2602_i2c_remove,
.id_table = ssm2602_i2c_id,
};
module_i2c_driver(ssm2602_i2c_driver);
diff --git a/sound/soc/codecs/ssm2602-spi.c b/sound/soc/codecs/ssm2602-spi.c
index b5df14fbe3ad..bb49fb6b62ad 100644
--- a/sound/soc/codecs/ssm2602-spi.c
+++ b/sound/soc/codecs/ssm2602-spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM2602 SPI audio driver
*
* Copyright 2014 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -20,12 +19,6 @@ static int ssm2602_spi_probe(struct spi_device *spi)
devm_regmap_init_spi(spi, &ssm2602_regmap_config));
}
-static int ssm2602_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
static const struct of_device_id ssm2602_of_match[] = {
{ .compatible = "adi,ssm2602", },
{ }
@@ -35,11 +28,9 @@ MODULE_DEVICE_TABLE(of, ssm2602_of_match);
static struct spi_driver ssm2602_spi_driver = {
.driver = {
.name = "ssm2602",
- .owner = THIS_MODULE,
.of_match_table = ssm2602_of_match,
},
.probe = ssm2602_spi_probe,
- .remove = ssm2602_spi_remove,
};
module_spi_driver(ssm2602_spi_driver);
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 314eaece1b7d..c29324403e9d 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -1,31 +1,17 @@
-/*
- * File: sound/soc/codecs/ssm2602.c
- * Author: Cliff Cai <Cliff.Cai@analog.com>
- *
- * 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/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// File: sound/soc/codecs/ssm2602.c
+// Author: Cliff Cai <Cliff.Cai@analog.com>
+//
+// 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 <linux/delay.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -54,10 +40,29 @@ struct ssm2602_priv {
* using 2 wire for device control, so we cache them instead.
* There is no point in caching the reset register
*/
-static const u16 ssm2602_reg[SSM2602_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+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 },
};
@@ -75,11 +80,10 @@ static const struct soc_enum ssm2602_enum[] = {
ssm2602_deemph),
};
-static const unsigned int ssm260x_outmix_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+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),
-};
+ 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);
@@ -105,7 +109,6 @@ SOC_SINGLE_TLV("Sidetone Playback Volume", SSM2602_APANA, 6, 3, 1,
SOC_SINGLE("Mic Boost (+20dB)", SSM2602_APANA, 0, 1, 0),
SOC_SINGLE("Mic Boost2 (+20dB)", SSM2602_APANA, 8, 1, 0),
-SOC_SINGLE("Mic Switch", SSM2602_APANA, 1, 1, 1),
};
/* Output Mixer */
@@ -115,10 +118,31 @@ 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),
@@ -140,6 +164,9 @@ SND_SOC_DAPM_MIXER("Output Mixer", SSM2602_PWR, 4, 1,
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"),
@@ -172,9 +199,11 @@ static const struct snd_soc_dapm_route ssm2602_routes[] = {
{"LHPOUT", NULL, "Output Mixer"},
{"Input Mux", "Line", "Line Input"},
- {"Input Mux", "Mic", "Mic Bias"},
+ {"Input Mux", "Mic", "Mic Switch"},
{"ADC", NULL, "Input Mux"},
+ {"Mic Switch", NULL, "Mic Bias"},
+
{"Mic Bias", NULL, "MICIN"},
};
@@ -263,9 +292,12 @@ 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 &&
- ssm2602_coeff_table[i].mclk == mclk)
- return ssm2602_coeff_table[i].srate;
+ 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;
}
@@ -274,8 +306,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -309,8 +341,8 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
static int ssm2602_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ 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,
@@ -321,9 +353,9 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
return 0;
}
-static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
+static int ssm2602_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(dai->component);
if (mute)
regmap_update_bits(ssm2602->regmap, SSM2602_APDIGI,
@@ -338,8 +370,8 @@ static int ssm2602_mute(struct snd_soc_dai *dai, int mute)
static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ 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)
@@ -348,18 +380,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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;
@@ -390,15 +428,15 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec_dai->codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBP_CFP:
iface |= 0x0040;
break;
- case SND_SOC_DAIFMT_CBS_CFS:
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -446,10 +484,10 @@ static int ssm2602_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
-static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
+static int ssm2602_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -473,7 +511,6 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -489,9 +526,10 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
- .digital_mute = ssm2602_mute,
+ .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 = {
@@ -509,23 +547,23 @@ static struct snd_soc_dai_driver ssm2602_dai = {
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = &ssm2602_dai_ops,
- .symmetric_rates = 1,
- .symmetric_samplebits = 1,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
};
-static int ssm2602_resume(struct snd_soc_codec *codec)
+static int ssm2602_resume(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ struct ssm2602_priv *ssm2602 = snd_soc_component_get_drvdata(component);
regcache_sync(ssm2602->regmap);
return 0;
}
-static int ssm2602_codec_probe(struct snd_soc_codec *codec)
+static int ssm2602_component_probe(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ 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,
@@ -533,7 +571,7 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
regmap_update_bits(ssm2602->regmap, SSM2602_ROUT1V,
ROUT1V_RLHP_BOTH, ROUT1V_RLHP_BOTH);
- ret = snd_soc_add_codec_controls(codec, ssm2602_snd_controls,
+ ret = snd_soc_add_component_controls(component, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
if (ret)
return ret;
@@ -547,9 +585,9 @@ static int ssm2602_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2602_routes));
}
-static int ssm2604_codec_probe(struct snd_soc_codec *codec)
+static int ssm2604_component_probe(struct snd_soc_component *component)
{
- struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
int ret;
ret = snd_soc_dapm_new_controls(dapm, ssm2604_dapm_widgets,
@@ -561,17 +599,20 @@ static int ssm2604_codec_probe(struct snd_soc_codec *codec)
ARRAY_SIZE(ssm2604_routes));
}
-static int ssm260x_codec_probe(struct snd_soc_codec *codec)
+static int ssm260x_component_probe(struct snd_soc_component *component)
{
- struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to issue reset: %d\n", ret);
+ 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);
@@ -583,28 +624,30 @@ static int ssm260x_codec_probe(struct snd_soc_codec *codec)
switch (ssm2602->type) {
case SSM2602:
- ret = ssm2602_codec_probe(codec);
+ ret = ssm2602_component_probe(component);
break;
case SSM2604:
- ret = ssm2604_codec_probe(codec);
+ ret = ssm2604_component_probe(component);
break;
}
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
- .probe = ssm260x_codec_probe,
- .resume = ssm2602_resume,
- .set_bias_level = ssm2602_set_bias_level,
- .suspend_bias_off = true,
-
- .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),
+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)
@@ -620,8 +663,8 @@ const struct regmap_config ssm2602_regmap_config = {
.volatile_reg = ssm2602_register_volatile,
.cache_type = REGCACHE_RBTREE,
- .reg_defaults_raw = ssm2602_reg,
- .num_reg_defaults_raw = ARRAY_SIZE(ssm2602_reg),
+ .reg_defaults = ssm2602_reg,
+ .num_reg_defaults = ARRAY_SIZE(ssm2602_reg),
};
EXPORT_SYMBOL_GPL(ssm2602_regmap_config);
@@ -641,7 +684,7 @@ int ssm2602_probe(struct device *dev, enum ssm2602_type type,
ssm2602->type = type;
ssm2602->regmap = regmap;
- return snd_soc_register_codec(dev, &soc_codec_dev_ssm2602,
+ return devm_snd_soc_register_component(dev, &soc_component_dev_ssm2602,
&ssm2602_dai, 1);
}
EXPORT_SYMBOL_GPL(ssm2602_probe);
diff --git a/sound/soc/codecs/ssm2602.h b/sound/soc/codecs/ssm2602.h
index 747538847689..05073380a4af 100644
--- a/sound/soc/codecs/ssm2602.h
+++ b/sound/soc/codecs/ssm2602.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* File: sound/soc/codecs/ssm2602.h
* Author: Cliff Cai <Cliff.Cai@analog.com>
@@ -8,21 +9,6 @@
* Copyright 2008 Analog Devices Inc.
*
* Bugs: Enter bugs at http://blackfin.uclinux.org/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see the file COPYING, or write
- * to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _SSM2602_H
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
new file mode 100644
index 000000000000..8c6665677a17
--- /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 <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+
+#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 <povik+lin@cutebit.org>");
+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
index a984485108cd..3e09c85abedb 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SSM4567 amplifier audio driver
*
@@ -6,10 +7,9 @@
*
* Based on code copyright/by:
* Copyright 2013 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
@@ -173,6 +173,12 @@ static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
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"),
};
@@ -180,13 +186,20 @@ 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_codec *codec = dai->codec;
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -207,9 +220,9 @@ static int ssm4567_hw_params(struct snd_pcm_substream *substream,
SSM4567_DAC_FS_MASK, dacfs);
}
-static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
+static int ssm4567_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(dai->codec);
+ struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(dai->component);
unsigned int val;
val = mute ? SSM4567_DAC_MUTE : 0;
@@ -265,8 +278,8 @@ static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
unsigned int ctrl1 = 0;
bool invert_fclk;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -315,7 +328,13 @@ static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
if (invert_fclk)
ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
- return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1);
+ 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)
@@ -332,6 +351,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
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);
@@ -341,10 +365,10 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
return ret;
}
-static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
+static int ssm4567_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct ssm4567 *ssm4567 = snd_soc_codec_get_drvdata(codec);
+ struct ssm4567 *ssm4567 = snd_soc_component_get_drvdata(component);
int ret = 0;
switch (level) {
@@ -353,7 +377,7 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
ret = ssm4567_set_power(ssm4567, true);
break;
case SND_SOC_BIAS_OFF:
@@ -361,19 +385,15 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
break;
}
- if (ret)
- return ret;
-
- codec->dapm.bias_level = level;
-
- return 0;
+ return ret;
}
static const struct snd_soc_dai_ops ssm4567_dai_ops = {
.hw_params = ssm4567_hw_params,
- .digital_mute = ssm4567_mute,
+ .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 = {
@@ -386,19 +406,27 @@ static struct snd_soc_dai_driver ssm4567_dai = {
.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 struct snd_soc_codec_driver ssm4567_codec_driver = {
- .set_bias_level = ssm4567_set_bias_level,
- .idle_bias_off = true,
-
- .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),
+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 = {
@@ -415,8 +443,7 @@ static const struct regmap_config ssm4567_regmap_config = {
.num_reg_defaults = ARRAY_SIZE(ssm4567_reg_defaults),
};
-static int ssm4567_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int ssm4567_i2c_probe(struct i2c_client *i2c)
{
struct ssm4567 *ssm4567;
int ret;
@@ -439,29 +466,41 @@ static int ssm4567_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &ssm4567_codec_driver,
+ return devm_snd_soc_register_component(&i2c->dev, &ssm4567_component_driver,
&ssm4567_dai, 1);
}
-static int ssm4567_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id ssm4567_i2c_ids[] = {
- { "ssm4567", 0 },
+ { "ssm4567" },
{ }
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ssm4567_of_match),
+ .acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
},
.probe = ssm4567_i2c_probe,
- .remove = ssm4567_i2c_remove,
.id_table = ssm4567_i2c_ids,
};
module_i2c_driver(ssm4567_driver);
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 3a1343fa109b..24d4b643917d 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
@@ -9,11 +10,6 @@
* Mark Brown <broonie@opensource.wolfsonmicro.com>
* Freescale Semiconductor, Inc.
* Timur Tabi <timur@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
@@ -21,11 +17,11 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -51,12 +47,9 @@
SNDRV_PCM_RATE_192000)
#define STA32X_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (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[] = {
@@ -106,13 +99,11 @@ static const struct reg_default sta32x_regs[] = {
};
static const struct regmap_range sta32x_write_regs_range[] = {
- regmap_reg_range(STA32X_CONFA, STA32X_AUTO2),
- regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2),
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
};
static const struct regmap_range sta32x_read_regs_range[] = {
- regmap_reg_range(STA32X_CONFA, STA32X_AUTO2),
- regmap_reg_range(STA32X_C1CFG, STA32X_FDRC2),
+ regmap_reg_range(STA32X_CONFA, STA32X_FDRC2),
};
static const struct regmap_range sta32x_volatile_regs_range[] = {
@@ -144,8 +135,9 @@ static const char *sta32x_supply_names[] = {
/* 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_codec *codec;
+ struct snd_soc_component *component;
struct sta32x_platform_data *pdata;
unsigned int mclk;
@@ -272,8 +264,8 @@ static int sta32x_coefficient_info(struct snd_kcontrol *kcontrol,
static int sta32x_coefficient_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -314,8 +306,8 @@ exit_unlock:
static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -349,9 +341,9 @@ static int sta32x_coefficient_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
+static int sta32x_sync_coef_shadow(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
unsigned int cfud;
int i;
@@ -377,16 +369,16 @@ static int sta32x_sync_coef_shadow(struct snd_soc_codec *codec)
return 0;
}
-static int sta32x_cache_sync(struct snd_soc_codec *codec)
+static int sta32x_cache_sync(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ sta32x_sync_coef_shadow(component);
rc = regcache_sync(sta32x->regmap);
regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
return rc;
@@ -397,17 +389,17 @@ static void sta32x_watchdog(struct work_struct *work)
{
struct sta32x_priv *sta32x = container_of(work, struct sta32x_priv,
watchdog_work.work);
- struct snd_soc_codec *codec = sta32x->codec;
+ struct snd_soc_component *component = sta32x->component;
unsigned int confa, confa_cached;
/* check if sta32x has reset itself */
- confa_cached = snd_soc_read(codec, STA32X_CONFA);
+ confa_cached = snd_soc_component_read(component, STA32X_CONFA);
regcache_cache_bypass(sta32x->regmap, true);
- confa = snd_soc_read(codec, STA32X_CONFA);
+ 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(codec);
+ sta32x_cache_sync(component);
}
if (!sta32x->shutdown)
@@ -584,10 +576,10 @@ static int mcs_ratio_table[3][7] = {
static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "mclk=%u\n", freq);
+ dev_dbg(component->dev, "mclk=%u\n", freq);
sta32x->mclk = freq;
return 0;
@@ -604,12 +596,12 @@ static int sta32x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int sta32x_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -653,22 +645,22 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev,
+ 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(codec->dev, "rate: %u, ratio: %u\n", rate, ratio);
+ 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) {
@@ -678,7 +670,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
}
if (ir < 0) {
- dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ dev_err(component->dev, "Unsupported samplerate: %u\n", rate);
return -EINVAL;
}
@@ -690,7 +682,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
}
if (mcs < 0) {
- dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ dev_err(component->dev, "Unresolvable ratio: %u\n", ratio);
return -EINVAL;
}
@@ -700,10 +692,10 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 24:
- dev_dbg(codec->dev, "24bit\n");
- /* fall through */
+ dev_dbg(component->dev, "24bit\n");
+ fallthrough;
case 32:
- dev_dbg(codec->dev, "24bit or 32bit\n");
+ dev_dbg(component->dev, "24bit or 32bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -718,7 +710,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 20:
- dev_dbg(codec->dev, "20bit\n");
+ dev_dbg(component->dev, "20bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x4;
@@ -733,7 +725,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 18:
- dev_dbg(codec->dev, "18bit\n");
+ dev_dbg(component->dev, "18bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x8;
@@ -748,7 +740,7 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
break;
case 16:
- dev_dbg(codec->dev, "16bit\n");
+ dev_dbg(component->dev, "16bit\n");
switch (sta32x->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -795,20 +787,20 @@ static int sta32x_startup_sequence(struct sta32x_priv *sta32x)
/**
* sta32x_set_bias_level - DAPM callback
- * @codec: the codec device
+ * @component: the component device
* @level: DAPM power level
*
- * This is called by ALSA to put the codec into low power mode
- * or to wake it up. If the codec is powered off completely
+ * 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_codec *codec,
+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_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "level = %d\n", level);
+ dev_dbg(component->dev, "level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
@@ -821,17 +813,17 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n", ret);
return ret;
}
sta32x_startup_sequence(sta32x);
- sta32x_cache_sync(codec);
+ sta32x_cache_sync(component);
sta32x_watchdog_start(sta32x);
}
@@ -849,14 +841,12 @@ static int sta32x_set_bias_level(struct snd_soc_codec *codec,
msleep(300);
sta32x_watchdog_stop(sta32x);
- if (sta32x->gpiod_nreset)
- gpiod_set_value(sta32x->gpiod_nreset, 0);
+ gpiod_set_value(sta32x->gpiod_nreset, 0);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
sta32x->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -878,22 +868,34 @@ static struct snd_soc_dai_driver sta32x_dai = {
.ops = &sta32x_dai_ops,
};
-static int sta32x_probe(struct snd_soc_codec *codec)
+static int sta32x_probe(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable supplies: %d\n", ret);
- return ret;
+ 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(codec->dev, "Failed to startup device\n");
- return ret;
+ dev_err(component->dev, "Failed to startup device\n");
+ goto err_regulator_bulk_disable;
}
/* CONFA */
@@ -972,34 +974,43 @@ static int sta32x_probe(struct snd_soc_codec *codec)
if (sta32x->pdata->needs_esd_watchdog)
INIT_DELAYED_WORK(&sta32x->watchdog_work, sta32x_watchdog);
- sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ 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:
+ clk_disable_unprepare(sta32x->xti_clk);
+ return ret;
}
-static int sta32x_remove(struct snd_soc_codec *codec)
+static void sta32x_remove(struct snd_soc_component *component)
{
- struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
+ struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
sta32x_watchdog_stop(sta32x);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
- return 0;
+ clk_disable_unprepare(sta32x->xti_clk);
}
-static const struct snd_soc_codec_driver sta32x_codec = {
- .probe = sta32x_probe,
- .remove = sta32x_remove,
- .set_bias_level = sta32x_set_bias_level,
- .suspend_bias_off = true,
- .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),
+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 = {
@@ -1008,7 +1019,7 @@ static const struct regmap_config sta32x_regmap = {
.max_register = STA32X_FDRC2,
.reg_defaults = sta32x_regs,
.num_reg_defaults = ARRAY_SIZE(sta32x_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta32x_write_regs,
.rd_table = &sta32x_read_regs,
.volatile_table = &sta32x_volatile_regs,
@@ -1040,33 +1051,32 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,needs_esd_watchdog", NULL))
- pdata->needs_esd_watchdog = 1;
+ 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 */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
+ 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 */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
sta32x->pdata = pdata;
@@ -1074,8 +1084,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
}
#endif
-static int sta32x_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta32x_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta32x_priv *sta32x;
@@ -1097,18 +1106,23 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
}
#endif
- /* GPIOs */
- sta32x->gpiod_nreset = devm_gpiod_get(dev, "reset");
- if (IS_ERR(sta32x->gpiod_nreset)) {
- ret = PTR_ERR(sta32x->gpiod_nreset);
- if (ret != -ENOENT && ret != -ENOSYS)
+ /* 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->gpiod_nreset = NULL;
- } else {
- gpiod_direction_output(sta32x->gpiod_nreset, 0);
+ 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];
@@ -1129,23 +1143,18 @@ static int sta32x_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta32x);
- ret = snd_soc_register_codec(dev, &sta32x_codec, &sta32x_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &sta32x_component,
+ &sta32x_dai, 1);
if (ret < 0)
- dev_err(dev, "Failed to register codec (%d)\n", ret);
+ dev_err(dev, "Failed to register component (%d)\n", ret);
return ret;
}
-static int sta32x_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
static const struct i2c_device_id sta32x_i2c_id[] = {
- { "sta326", 0 },
- { "sta328", 0 },
- { "sta329", 0 },
+ { "sta326" },
+ { "sta328" },
+ { "sta329" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
@@ -1153,11 +1162,9 @@ MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
static struct i2c_driver sta32x_i2c_driver = {
.driver = {
.name = "sta32x",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(st32x_dt_ids),
},
- .probe = sta32x_i2c_probe,
- .remove = sta32x_i2c_remove,
+ .probe = sta32x_i2c_probe,
.id_table = sta32x_i2c_id,
};
diff --git a/sound/soc/codecs/sta32x.h b/sound/soc/codecs/sta32x.h
index d3191c983d71..1b90d7460b4b 100644
--- a/sound/soc/codecs/sta32x.h
+++ b/sound/soc/codecs/sta32x.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Codec driver for ST STA32x 2.1-channel high-efficiency digital audio system
*
@@ -7,11 +8,6 @@
* based on code from:
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#ifndef _ASOC_STA_32X_H
#define _ASOC_STA_32X_H
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index bda2ee18769e..d1450de92652 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
*
@@ -11,11 +12,6 @@
* Mark Brown <broonie@opensource.wolfsonmicro.com>
* Freescale Semiconductor, Inc.
* Timur Tabi <timur@freescale.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__
@@ -26,8 +22,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
-#include <linux/of_device.h>
-#include <linux/of_gpio.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio/consumer.h>
@@ -52,12 +47,9 @@
SNDRV_PCM_RATE_192000)
#define STA350_FORMATS \
- (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \
- SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
- SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE | \
- SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
+ (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[] = {
@@ -309,8 +301,8 @@ static int sta350_coefficient_info(struct snd_kcontrol *kcontrol,
static int sta350_coefficient_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -351,8 +343,8 @@ exit_unlock:
static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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;
@@ -386,9 +378,9 @@ static int sta350_coefficient_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
+static int sta350_sync_coef_shadow(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
unsigned int cfud;
int i;
@@ -414,16 +406,16 @@ static int sta350_sync_coef_shadow(struct snd_soc_codec *codec)
return 0;
}
-static int sta350_cache_sync(struct snd_soc_codec *codec)
+static int sta350_cache_sync(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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(codec);
+ sta350_sync_coef_shadow(component);
rc = regcache_sync(sta350->regmap);
regmap_write(sta350->regmap, STA350_MMUTE, mute);
return rc;
@@ -613,10 +605,10 @@ static int mcs_ratio_table[3][6] = {
static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = codec_dai->component;
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
- dev_dbg(codec->dev, "mclk=%u\n", freq);
+ dev_dbg(component->dev, "mclk=%u\n", freq);
sta350->mclk = freq;
return 0;
@@ -633,12 +625,12 @@ static int sta350_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int sta350_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
break;
default:
return -EINVAL;
@@ -682,22 +674,22 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev,
+ 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(codec->dev, "rate: %u, ratio: %u\n", rate, ratio);
+ 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) {
@@ -707,7 +699,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
}
if (ir < 0) {
- dev_err(codec->dev, "Unsupported samplerate: %u\n", rate);
+ dev_err(component->dev, "Unsupported samplerate: %u\n", rate);
return -EINVAL;
}
@@ -719,7 +711,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
}
if (mcs < 0) {
- dev_err(codec->dev, "Unresolvable ratio: %u\n", ratio);
+ dev_err(component->dev, "Unresolvable ratio: %u\n", ratio);
return -EINVAL;
}
@@ -729,10 +721,10 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
switch (params_width(params)) {
case 24:
- dev_dbg(codec->dev, "24bit\n");
- /* fall through */
+ dev_dbg(component->dev, "24bit\n");
+ fallthrough;
case 32:
- dev_dbg(codec->dev, "24bit or 32bit\n");
+ dev_dbg(component->dev, "24bit or 32bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -747,7 +739,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 20:
- dev_dbg(codec->dev, "20bit\n");
+ dev_dbg(component->dev, "20bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x4;
@@ -762,7 +754,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 18:
- dev_dbg(codec->dev, "18bit\n");
+ dev_dbg(component->dev, "18bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x8;
@@ -777,7 +769,7 @@ static int sta350_hw_params(struct snd_pcm_substream *substream,
break;
case 16:
- dev_dbg(codec->dev, "16bit\n");
+ dev_dbg(component->dev, "16bit\n");
switch (sta350->format) {
case SND_SOC_DAIFMT_I2S:
confb |= 0x0;
@@ -827,20 +819,20 @@ static int sta350_startup_sequence(struct sta350_priv *sta350)
/**
* sta350_set_bias_level - DAPM callback
- * @codec: the codec device
+ * @component: the component device
* @level: DAPM power level
*
- * This is called by ALSA to put the codec into low power mode
- * or to wake it up. If the codec is powered off completely
+ * 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_codec *codec,
+static int sta350_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
int ret;
- dev_dbg(codec->dev, "level = %d\n", level);
+ dev_dbg(component->dev, "level = %d\n", level);
switch (level) {
case SND_SOC_BIAS_ON:
break;
@@ -853,18 +845,18 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+ 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(codec->dev,
+ dev_err(component->dev,
"Failed to enable supplies: %d\n",
ret);
return ret;
}
sta350_startup_sequence(sta350);
- sta350_cache_sync(codec);
+ sta350_cache_sync(component);
}
/* Power down */
@@ -890,7 +882,6 @@ static int sta350_set_bias_level(struct snd_soc_codec *codec,
sta350->supplies);
break;
}
- codec->dapm.bias_level = level;
return 0;
}
@@ -912,22 +903,22 @@ static struct snd_soc_dai_driver sta350_dai = {
.ops = &sta350_dai_ops,
};
-static int sta350_probe(struct snd_soc_codec *codec)
+static int sta350_probe(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable supplies: %d\n", ret);
+ dev_err(component->dev, "Failed to enable supplies: %d\n", ret);
return ret;
}
ret = sta350_startup_sequence(sta350);
if (ret < 0) {
- dev_err(codec->dev, "Failed to startup device\n");
+ dev_err(component->dev, "Failed to startup device\n");
return ret;
}
@@ -1037,33 +1028,34 @@ static int sta350_probe(struct snd_soc_codec *codec)
sta350->coef_shadow[60] = 0x400000;
sta350->coef_shadow[61] = 0x400000;
- sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ 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 int sta350_remove(struct snd_soc_codec *codec)
+static void sta350_remove(struct snd_soc_component *component)
{
- struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
+ struct sta350_priv *sta350 = snd_soc_component_get_drvdata(component);
regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
-
- return 0;
}
-static const struct snd_soc_codec_driver sta350_codec = {
- .probe = sta350_probe,
- .remove = sta350_remove,
- .set_bias_level = sta350_set_bias_level,
- .suspend_bias_off = true,
- .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),
+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 = {
@@ -1072,7 +1064,7 @@ static const struct regmap_config sta350_regmap = {
.max_register = STA350_MISC2,
.reg_defaults = sta350_regs,
.num_reg_defaults = ARRAY_SIZE(sta350_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta350_write_regs,
.rd_table = &sta350_read_regs,
.volatile_table = &sta350_volatile_regs,
@@ -1113,12 +1105,12 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,fault-detect-recovery", NULL))
- pdata->fault_detect_recovery = 1;
+ 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",
@@ -1140,41 +1132,34 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
- if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
- pdata->oc_warning_adjustment = 1;
+ pdata->oc_warning_adjustment =
+ of_property_read_bool(np, "st,overcurrent-warning-adjustment");
/* CONFE */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
-
- if (of_get_property(np, "st,distortion-compensation", NULL))
- pdata->distortion_compensation = 1;
+ 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 */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
/* MISC */
- if (of_get_property(np, "st,activate-mute-output", NULL))
- pdata->activate_mute_output = 1;
-
- if (of_get_property(np, "st,bridge-immediate-off", NULL))
- pdata->bridge_immediate_off = 1;
-
- if (of_get_property(np, "st,noise-shape-dc-cut", NULL))
- pdata->noise_shape_dc_cut = 1;
-
- if (of_get_property(np, "st,powerdown-master-volume", NULL))
- pdata->powerdown_master_vol = 1;
+ 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)
@@ -1190,8 +1175,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
}
#endif
-static int sta350_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static int sta350_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct sta350_priv *sta350;
@@ -1213,27 +1197,15 @@ static int sta350_i2c_probe(struct i2c_client *i2c,
#endif
/* GPIOs */
- sta350->gpiod_nreset = devm_gpiod_get(dev, "reset");
- if (IS_ERR(sta350->gpiod_nreset)) {
- ret = PTR_ERR(sta350->gpiod_nreset);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
+ 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_nreset = NULL;
- } else {
- gpiod_direction_output(sta350->gpiod_nreset, 0);
- }
-
- sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down");
- if (IS_ERR(sta350->gpiod_power_down)) {
- ret = PTR_ERR(sta350->gpiod_power_down);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- sta350->gpiod_power_down = NULL;
- } else {
- gpiod_direction_output(sta350->gpiod_power_down, 0);
- }
+ 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++)
@@ -1255,21 +1227,18 @@ static int sta350_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta350);
- ret = snd_soc_register_codec(dev, &sta350_codec, &sta350_dai, 1);
+ ret = devm_snd_soc_register_component(dev, &sta350_component, &sta350_dai, 1);
if (ret < 0)
- dev_err(dev, "Failed to register codec (%d)\n", ret);
+ dev_err(dev, "Failed to register component (%d)\n", ret);
return ret;
}
-static int sta350_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
+static void sta350_i2c_remove(struct i2c_client *client)
+{}
static const struct i2c_device_id sta350_i2c_id[] = {
- { "sta350", 0 },
+ { "sta350" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
@@ -1277,7 +1246,6 @@ MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
static struct i2c_driver sta350_i2c_driver = {
.driver = {
.name = "sta350",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(st350_dt_ids),
},
.probe = sta350_i2c_probe,
diff --git a/sound/soc/codecs/sta350.h b/sound/soc/codecs/sta350.h
index fb7285290779..80bf56093d94 100644
--- a/sound/soc/codecs/sta350.h
+++ b/sound/soc/codecs/sta350.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Codec driver for ST STA350 2.1-channel high-efficiency digital audio system
*
@@ -9,16 +10,11 @@
* Johannes Stezenbach <js@sig21.net>
* Wolfson Microelectronics PLC.
* Mark Brown <broonie@opensource.wolfsonmicro.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
*/
#ifndef _ASOC_STA_350_H
#define _ASOC_STA_350_H
-/* STA50 register addresses */
+/* STA350 register addresses */
#define STA350_REGISTER_COUNT 0x4D
#define STA350_COEF_COUNT 62
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index b0f436d10125..f7718491c899 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -151,40 +151,34 @@ static const struct snd_kcontrol_new sta529_snd_controls[] = {
SOC_ENUM("PWM Select", pwm_src),
};
-static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
+static int sta529_set_bias_level(struct snd_soc_component *component, enum
snd_soc_bias_level level)
{
- struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec);
+ struct sta529 *sta529 = snd_soc_component_get_drvdata(component);
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0, POWER_CNTLMSAK,
POWER_UP);
- snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK,
FFX_CLK_ENB);
break;
case SND_SOC_BIAS_STANDBY:
- if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
regcache_sync(sta529->regmap);
- snd_soc_update_bits(codec, STA529_FFXCFG0,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0,
POWER_CNTLMSAK, POWER_STDBY);
/* Making FFX output to zero */
- snd_soc_update_bits(codec, STA529_FFXCFG0, FFX_MASK,
+ snd_soc_component_update_bits(component, STA529_FFXCFG0, FFX_MASK,
FFX_OFF);
- snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, FFX_CLK_MSK,
FFX_CLK_DIS);
break;
case SND_SOC_BIAS_OFF:
break;
}
- /*
- * store the label for powers down audio subsystem for suspend.This is
- * used by soc core layer
- */
- codec->dapm.bias_level = level;
-
return 0;
}
@@ -193,7 +187,7 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
int pdata, play_freq_val, record_freq_val;
int bclk_to_fs_ratio;
@@ -211,7 +205,7 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
bclk_to_fs_ratio = 2;
break;
default:
- dev_err(codec->dev, "Unsupported format\n");
+ dev_err(component->dev, "Unsupported format\n");
return -EINVAL;
}
@@ -234,44 +228,44 @@ static int sta529_hw_params(struct snd_pcm_substream *substream,
record_freq_val = 0;
break;
default:
- dev_err(codec->dev, "Unsupported rate\n");
+ dev_err(component->dev, "Unsupported rate\n");
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- snd_soc_update_bits(codec, STA529_S2PCFG1, PDATA_LEN_MSK,
+ snd_soc_component_update_bits(component, STA529_S2PCFG1, PDATA_LEN_MSK,
pdata << 6);
- snd_soc_update_bits(codec, STA529_S2PCFG1, BCLK_TO_FS_MSK,
+ snd_soc_component_update_bits(component, STA529_S2PCFG1, BCLK_TO_FS_MSK,
bclk_to_fs_ratio << 4);
- snd_soc_update_bits(codec, STA529_MISC, PLAY_FREQ_RANGE_MSK,
+ snd_soc_component_update_bits(component, STA529_MISC, PLAY_FREQ_RANGE_MSK,
play_freq_val << 4);
} else {
- snd_soc_update_bits(codec, STA529_P2SCFG1, PDATA_LEN_MSK,
+ snd_soc_component_update_bits(component, STA529_P2SCFG1, PDATA_LEN_MSK,
pdata << 6);
- snd_soc_update_bits(codec, STA529_P2SCFG1, BCLK_TO_FS_MSK,
+ snd_soc_component_update_bits(component, STA529_P2SCFG1, BCLK_TO_FS_MSK,
bclk_to_fs_ratio << 4);
- snd_soc_update_bits(codec, STA529_MISC, CAP_FREQ_RANGE_MSK,
+ 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)
+static int sta529_mute(struct snd_soc_dai *dai, int mute, int direction)
{
u8 val = 0;
if (mute)
val |= CODEC_MUTE_VAL;
- snd_soc_update_bits(dai->codec, STA529_FFXCFG0, AUDIO_MUTE_MSK, 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_codec *codec = codec_dai->codec;
+ struct snd_soc_component *component = codec_dai->component;
u8 mode = 0;
/* interface format */
@@ -289,7 +283,7 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
+ snd_soc_component_update_bits(component, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
return 0;
}
@@ -297,7 +291,8 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
static const struct snd_soc_dai_ops sta529_dai_ops = {
.hw_params = sta529_hw_params,
.set_fmt = sta529_set_dai_fmt,
- .digital_mute = sta529_mute,
+ .mute_stream = sta529_mute,
+ .no_capture_mute = 1,
};
static struct snd_soc_dai_driver sta529_dai = {
@@ -319,12 +314,14 @@ static struct snd_soc_dai_driver sta529_dai = {
.ops = &sta529_dai_ops,
};
-static const struct snd_soc_codec_driver sta529_codec_driver = {
- .set_bias_level = sta529_set_bias_level,
- .suspend_bias_off = true,
-
- .controls = sta529_snd_controls,
- .num_controls = ARRAY_SIZE(sta529_snd_controls),
+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 = {
@@ -334,20 +331,16 @@ static const struct regmap_config sta529_regmap = {
.max_register = STA529_MAX_REGISTER,
.readable_reg = sta529_readable,
- .cache_type = REGCACHE_RBTREE,
+ .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,
- const struct i2c_device_id *id)
+static int sta529_i2c_probe(struct i2c_client *i2c)
{
struct sta529 *sta529;
int ret;
- if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
- return -EINVAL;
-
sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL);
if (!sta529)
return -ENOMEM;
@@ -361,34 +354,32 @@ static int sta529_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, sta529);
- ret = snd_soc_register_codec(&i2c->dev,
- &sta529_codec_driver, &sta529_dai, 1);
+ 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 int sta529_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id sta529_i2c_id[] = {
- { "sta529", 0 },
+ { "sta529" },
{ }
};
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",
- .owner = THIS_MODULE,
+ .of_match_table = sta529_of_match,
},
.probe = sta529_i2c_probe,
- .remove = sta529_i2c_remove,
.id_table = sta529_i2c_id,
};
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 6464caf72b21..2f9f10a4dfed 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* stac9766.c -- ALSA SoC STAC9766 codec support
*
* Copyright 2009 Jon Smirl, Digispeaker
* Author: Jon Smirl <jonsmirl@gmail.com>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
* Features:-
*
* o Support for AC97 Codec, S/PDIF
@@ -18,6 +14,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -26,28 +23,56 @@
#include <sound/soc.h>
#include <sound/tlv.h>
-#include "stac9766.h"
+#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 },
+};
-/*
- * STAC9766 register cache
- */
-static const u16 stac9766_reg[] = {
- 0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */
- 0x0000, 0x0000, 0x8008, 0x8008, /* e */
- 0x8808, 0x8808, 0x8808, 0x8808, /* 16 */
- 0x8808, 0x0000, 0x8000, 0x0000, /* 1e */
- 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
- 0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */
- 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
- 0x0000, 0x2000, 0x0000, 0x0100, /* 3e */
- 0x0000, 0x0000, 0x0080, 0x0000, /* 46 */
- 0x0000, 0x0000, 0x0003, 0xffff, /* 4e */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 56 */
- 0x4000, 0x0000, 0x0000, 0x0000, /* 5e */
- 0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
- 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 7e */
+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",
@@ -82,10 +107,10 @@ static SOC_ENUM_SINGLE_DECL(stac9766_boost2_enum,
static SOC_ENUM_SINGLE_DECL(stac9766_stereo_mic_enum,
AC97_STAC_STEREO_MIC, 2, stac9766_stereo_mic);
-static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
-static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
-static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
-static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
+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),
@@ -136,149 +161,64 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
};
-static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 *cache = codec->reg_cache;
-
- if (reg > AC97_STAC_PAGE0) {
- stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops->write(ac97, reg, val);
- stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
- return 0;
- }
- if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
- return -EIO;
-
- soc_ac97_ops->write(ac97, reg, val);
- cache[reg / 2] = val;
- return 0;
-}
-
-static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 val = 0, *cache = codec->reg_cache;
-
- if (reg > AC97_STAC_PAGE0) {
- stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
- stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
- return val;
- }
- if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
- return -EIO;
-
- if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
- reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
- reg == AC97_VENDOR_ID2) {
-
- val = soc_ac97_ops->read(ac97, reg);
- return val;
- }
- return cache[reg / 2];
-}
-
static int ac97_analog_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned short reg, vra;
-
- vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+ unsigned short reg;
- vra |= 0x1; /* enable variable rate audio */
- vra &= ~0x4; /* disable SPDIF output */
-
- stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+ /* 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 stac9766_ac97_write(codec, reg, runtime->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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
struct snd_pcm_runtime *runtime = substream->runtime;
- unsigned short reg, vra;
-
- stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
+ unsigned short reg;
- vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
- vra |= 0x5; /* Enable VRA and SPDIF out */
+ snd_soc_component_write(component, AC97_SPDIF, 0x2002);
- stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+ /* Enable VRA and SPDIF out */
+ snd_soc_component_update_bits(component, AC97_EXTENDED_STATUS, 0x5, 0x5);
reg = AC97_PCM_FRONT_DAC_RATE;
- return stac9766_ac97_write(codec, reg, runtime->rate);
+ return snd_soc_component_write(component, reg, runtime->rate);
}
-static int stac9766_set_bias_level(struct snd_soc_codec *codec,
+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 */
- stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0x0000);
break;
case SND_SOC_BIAS_OFF: /* Off, without power */
/* disable everything including AC link */
- stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
+ snd_soc_component_write(component, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm.bias_level = level;
- return 0;
-}
-
-static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
-{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
-
- if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(ac97);
- if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
- return 1;
- }
-
- soc_ac97_ops->reset(ac97);
- if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(ac97);
- if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
- return -EIO;
return 0;
}
-static int stac9766_codec_resume(struct snd_soc_codec *codec)
+static int stac9766_component_resume(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
- u16 id, reset;
-
- reset = 0;
- /* give the codec an AC97 warm reset to start the link */
-reset:
- if (reset > 5) {
- dev_err(codec->dev, "Failed to resume\n");
- return -EIO;
- }
- ac97->bus->ops->warm_reset(ac97);
- id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
- if (id != 0x4c13) {
- stac9766_reset(codec, 0);
- reset++;
- goto reset;
- }
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- return 0;
+ return snd_ac97_reset(ac97, true, STAC9766_VENDOR_ID,
+ STAC9766_VENDOR_ID_MASK);
}
static const struct snd_soc_dai_ops stac9766_dai_ops_analog = {
@@ -321,74 +261,64 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
.channels_max = 2,
.rates = SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
},
/* alsa ops */
.ops = &stac9766_dai_ops_digital,
}
};
-static int stac9766_codec_probe(struct snd_soc_codec *codec)
+static int stac9766_component_probe(struct snd_soc_component *component)
{
struct snd_ac97 *ac97;
- int ret = 0;
+ struct regmap *regmap;
+ int ret;
- ac97 = snd_soc_new_ac97_codec(codec);
+ ac97 = snd_soc_new_ac97_component(component, STAC9766_VENDOR_ID,
+ STAC9766_VENDOR_ID_MASK);
if (IS_ERR(ac97))
return PTR_ERR(ac97);
- snd_soc_codec_set_drvdata(codec, ac97);
-
- /* do a cold reset for the controller and then try
- * a warm reset followed by an optional cold reset for codec */
- stac9766_reset(codec, 0);
- ret = stac9766_reset(codec, 1);
- if (ret < 0) {
- dev_err(codec->dev, "Failed to reset: AC97 link error\n");
- goto codec_err;
+ regmap = regmap_init_ac97(ac97, &stac9766_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
}
- return 0;
+ snd_soc_component_init_regmap(component, regmap);
+ snd_soc_component_set_drvdata(component, ac97);
-codec_err:
- snd_soc_free_ac97_codec(ac97);
+ return 0;
+err_free_ac97:
+ snd_soc_free_ac97_component(ac97);
return ret;
}
-static int stac9766_codec_remove(struct snd_soc_codec *codec)
+static void stac9766_component_remove(struct snd_soc_component *component)
{
- struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+ struct snd_ac97 *ac97 = snd_soc_component_get_drvdata(component);
- snd_soc_free_ac97_codec(ac97);
- return 0;
+ snd_soc_component_exit_regmap(component);
+ snd_soc_free_ac97_component(ac97);
}
-static struct snd_soc_codec_driver soc_codec_dev_stac9766 = {
- .controls = stac9766_snd_ac97_controls,
- .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
- .write = stac9766_ac97_write,
- .read = stac9766_ac97_read,
- .set_bias_level = stac9766_set_bias_level,
- .suspend_bias_off = true,
- .probe = stac9766_codec_probe,
- .remove = stac9766_codec_remove,
- .resume = stac9766_codec_resume,
- .reg_cache_size = ARRAY_SIZE(stac9766_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_step = 2,
- .reg_cache_default = stac9766_reg,
+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 snd_soc_register_codec(&pdev->dev,
- &soc_codec_dev_stac9766, stac9766_dai, ARRAY_SIZE(stac9766_dai));
-}
-
-static int stac9766_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_codec(&pdev->dev);
- return 0;
+ 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 = {
@@ -397,7 +327,6 @@ static struct platform_driver stac9766_codec_driver = {
},
.probe = stac9766_probe,
- .remove = stac9766_remove,
};
module_platform_driver(stac9766_codec_driver);
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h
deleted file mode 100644
index c726f907e2c0..000000000000
--- a/sound/soc/codecs/stac9766.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * stac9766.h -- STAC9766 Soc Audio driver
- */
-
-#ifndef _STAC9766_H
-#define _STAC9766_H
-
-#define AC97_STAC_PAGE0 0x1000
-#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A)
-#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E)
-#define AC97_STAC_STEREO_MIC 0x78
-
-/* STAC9766 DAI ID's */
-#define STAC9766_DAI_AC97_ANALOG 0
-#define STAC9766_DAI_AC97_DIGITAL 1
-
-#endif
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
new file mode 100644
index 000000000000..4ab15be69f3a
--- /dev/null
+++ b/sound/soc/codecs/sti-sas.c
@@ -0,0 +1,465 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) STMicroelectronics SA 2015
+ * Authors: Arnaud Pouliquen <arnaud.pouliquen@st.com>
+ * for STMicroelectronics.
+ */
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+/* 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 */
+};
+
+/* 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,
+};
+
+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 const 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,
+ .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 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;
+
+ /* 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
index ae23acdd2708..43449d7c2584 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -1,26 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tas2552.c - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 - 2024 Texas Instruments Incorporated -
+ * https://www.ti.com
*
* Author: Dan Murphy <dmurphy@ti.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/device.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -34,18 +25,19 @@
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
#include <sound/tas2552-plat.h>
+#include <dt-bindings/sound/tas2552.h>
#include "tas2552.h"
-static struct reg_default tas2552_reg_defs[] = {
+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_PT_CTRL, 0x0f},
- {TAS2552_RESERVED_0D, 0x00},
+ {TAS2552_BOOST_APT_CTRL, 0x0f},
+ {TAS2552_RESERVED_0D, 0xbe},
{TAS2552_LIMIT_RATE_HYS, 0x08},
{TAS2552_CFG_2, 0xef},
{TAS2552_SER_CTRL_1, 0x00},
@@ -69,26 +61,53 @@ static const char *tas2552_supply_names[TAS2552_NUM_SUPPLIES] = {
};
struct tas2552_data {
- struct snd_soc_codec *codec;
+ 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 mclk;
-};
+ unsigned int pll_clkin;
+ int pll_clk_id;
+ unsigned int pdm_clk;
+ int pdm_clk_id;
-/* Input mux controls */
-static const char *tas2552_input_texts[] = {
- "Digital", "Analog"
+ 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("Input selection", tas2552_input_mux_enum)
-};
+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[] =
{
@@ -96,14 +115,17 @@ static const struct snd_soc_dapm_widget tas2552_dapm_widgets[] =
/* MUX Controls */
SND_SOC_DAPM_MUX("Input selection", SND_SOC_NOPM, 0, 0,
- tas2552_input_mux_control),
+ &tas2552_input_mux_control),
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI OUT", "DAC Capture", 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")
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_INPUT("DMIC")
};
static const struct snd_soc_dapm_route tas2552_audio_map[] = {
@@ -113,169 +135,359 @@ static const struct snd_soc_dapm_route tas2552_audio_map[] = {
{"ClassD", NULL, "Input selection"},
{"OUT", NULL, "ClassD"},
{"ClassD", NULL, "PLL"},
+ {"ASI OUT", NULL, "DMIC"}
};
-#ifdef CONFIG_PM
-static void tas2552_sw_shutdown(struct tas2552_data *tas_data, int sw_shutdown)
+static void tas2552_sw_shutdown(struct tas2552_data *tas2552, int sw_shutdown)
{
- u8 cfg1_reg;
+ u8 cfg1_reg = 0;
+
+ if (!tas2552->component)
+ return;
if (sw_shutdown)
- cfg1_reg = 0;
- else
- cfg1_reg = TAS2552_SWS_MASK;
+ cfg1_reg = TAS2552_SWS;
- snd_soc_update_bits(tas_data->codec, TAS2552_CFG_1,
- TAS2552_SWS_MASK, cfg1_reg);
+ snd_soc_component_update_bits(tas2552->component, TAS2552_CFG_1, TAS2552_SWS,
+ cfg1_reg);
}
-#endif
-static int tas2552_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
+static int tas2552_setup_pll(struct snd_soc_component *component,
+ struct snd_pcm_hw_params *params)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
- int sample_rate, pll_clk;
- int d;
- u8 p, j;
+ 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;
- if (!tas2552->mclk)
- return -EINVAL;
+ pll_clkin = snd_soc_params_to_bclk(params);
+ pll_clkin += tas2552->tdm_delay;
+ }
- snd_soc_update_bits(codec, TAS2552_CFG_2, TAS2552_PLL_ENABLE, 0);
+ 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 (tas2552->mclk == TAS2552_245MHZ_CLK ||
- tas2552->mclk == TAS2552_225MHZ_CLK) {
+ if (pll_clkin == pll_clk)
+ bypass_pll = true;
+
+ if (bypass_pll) {
/* By pass the PLL configuration */
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_2,
- TAS2552_PLL_BYPASS_MASK,
- TAS2552_PLL_BYPASS);
+ 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 * freq * J.D) / 2^p
+ * pll_clk = (.5 * pll_clkin * J.D) / 2^p
* Need to fill in J and D here based on incoming freq
*/
- p = snd_soc_read(codec, TAS2552_PLL_CTRL_1);
+ 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);
- sample_rate = params_rate(params);
-
- if (sample_rate == 48000)
- pll_clk = TAS2552_245MHZ_CLK;
- else if (sample_rate == 44100)
- pll_clk = TAS2552_225MHZ_CLK;
- else {
- dev_vdbg(codec->dev, "Substream sample rate is not found %i\n",
- params_rate(params));
- return -EINVAL;
+
+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;
}
- j = (pll_clk * 2 * (1 << p)) / tas2552->mclk;
- d = (pll_clk * 2 * (1 << p)) % tas2552->mclk;
+ snd_soc_component_update_bits(component, TAS2552_CFG_1, TAS2552_PLL_SRC_MASK,
+ pll_sel);
- snd_soc_update_bits(codec, TAS2552_PLL_CTRL_1,
- TAS2552_PLL_J_MASK, j);
- snd_soc_write(codec, TAS2552_PLL_CTRL_2,
- (d >> 7) & TAS2552_PLL_D_UPPER_MASK);
- snd_soc_write(codec, TAS2552_PLL_CTRL_3,
- d & TAS2552_PLL_D_LOWER_MASK);
+ 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_codec *codec = dai->codec;
+ struct snd_soc_component *component = dai->component;
+ struct tas2552_data *tas2552 = dev_get_drvdata(component->dev);
u8 serial_format;
- u8 serial_control_mask;
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBS_CFS:
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
serial_format = 0x00;
break;
- case SND_SOC_DAIFMT_CBS_CFM:
- serial_format = TAS2552_WORD_CLK_MASK;
+ case SND_SOC_DAIFMT_CBC_CFP:
+ serial_format = TAS2552_WCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFS:
- serial_format = TAS2552_BIT_CLK_MASK;
+ case SND_SOC_DAIFMT_CBP_CFC:
+ serial_format = TAS2552_BCLKDIR;
break;
- case SND_SOC_DAIFMT_CBM_CFM:
- serial_format = (TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK);
+ case SND_SOC_DAIFMT_CBP_CFP:
+ serial_format = (TAS2552_BCLKDIR | TAS2552_WCLKDIR);
break;
default:
- dev_vdbg(codec->dev, "DAI Format master is not found\n");
+ dev_vdbg(component->dev, "DAI Format master is not found\n");
return -EINVAL;
}
- serial_control_mask = TAS2552_BIT_CLK_MASK | TAS2552_WORD_CLK_MASK;
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- serial_format &= TAS2552_DAIFMT_I2S_MASK;
+ 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:
- serial_format |= TAS2552_DAIFMT_DSP;
+ 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:
- serial_format |= TAS2552_DAIFMT_RIGHT_J;
+ case (SND_SOC_DAIFMT_RIGHT_J | SND_SOC_DAIFMT_NB_NF):
+ serial_format |= TAS2552_DATAFORMAT_RIGHT_J;
break;
- case SND_SOC_DAIFMT_LEFT_J:
- serial_format |= TAS2552_DAIFMT_LEFT_J;
+ case (SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_NB_NF):
+ serial_format |= TAS2552_DATAFORMAT_LEFT_J;
break;
default:
- dev_vdbg(codec->dev, "DAI Format is not found\n");
+ dev_vdbg(component->dev, "DAI Format is not found\n");
return -EINVAL;
}
+ tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- if (fmt & SND_SOC_DAIFMT_FORMAT_MASK)
- serial_control_mask |= TAS2552_DATA_FORMAT_MASK;
-
- snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, serial_control_mask,
- serial_format);
-
+ 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_codec *codec = dai->codec;
- struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
+ 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;
- tas2552->mclk = freq;
+ 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)
+static int tas2552_mute(struct snd_soc_dai *dai, int mute, int direction)
{
- u8 cfg1_reg;
- struct snd_soc_codec *codec = dai->codec;
+ u8 cfg1_reg = 0;
+ struct snd_soc_component *component = dai->component;
if (mute)
- cfg1_reg = TAS2552_MUTE_MASK;
- else
- cfg1_reg = ~TAS2552_MUTE_MASK;
+ cfg1_reg |= TAS2552_MUTE;
- snd_soc_update_bits(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK, cfg1_reg);
+ 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, 0);
+ tas2552_sw_shutdown(tas2552, 1);
regcache_cache_only(tas2552->regmap, true);
regcache_mark_dirty(tas2552->regmap);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 0);
+ gpiod_set_value(tas2552->enable_gpio, 0);
return 0;
}
@@ -284,28 +496,28 @@ static int tas2552_runtime_resume(struct device *dev)
{
struct tas2552_data *tas2552 = dev_get_drvdata(dev);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 1);
+ gpiod_set_value(tas2552->enable_gpio, 1);
- tas2552_sw_shutdown(tas2552, 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)
+ RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, NULL)
};
-static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
+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,
- .digital_mute = tas2552_mute,
+ .set_tdm_slot = tas2552_set_dai_tdm_slot,
+ .mute_stream = tas2552_mute,
+ .no_capture_mute = 1,
};
/* Formats supported by TAS2552 driver. */
@@ -323,6 +535,13 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = TAS2552_FORMATS,
},
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = TAS2552_FORMATS,
+ },
.ops = &tas2552_speaker_dai_ops,
},
};
@@ -330,136 +549,129 @@ static struct snd_soc_dai_driver tas2552_dai[] = {
/*
* DAC digital volumes. From -7 to 24 dB in 1 dB steps
*/
-static DECLARE_TLV_DB_SCALE(dac_tlv, -7, 100, 24);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -700, 100, 0);
-static const struct snd_kcontrol_new tas2552_snd_controls[] = {
- SOC_SINGLE_TLV("Speaker Driver Playback Volume",
- TAS2552_PGA_GAIN, 0, 0x1f, 1, dac_tlv),
- SOC_DAPM_SINGLE("Playback AMP", SND_SOC_NOPM, 0, 1, 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 reg_default tas2552_init_regs[] = {
- { TAS2552_RESERVED_0D, 0xc0 },
+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_codec_probe(struct snd_soc_codec *codec)
+static int tas2552_component_probe(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
int ret;
- tas2552->codec = codec;
+ tas2552->component = component;
ret = regulator_bulk_enable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
if (ret != 0) {
- dev_err(codec->dev, "Failed to enable supplies: %d\n",
+ dev_err(component->dev, "Failed to enable supplies: %d\n",
ret);
return ret;
}
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 1);
+ gpiod_set_value(tas2552->enable_gpio, 1);
- ret = pm_runtime_get_sync(codec->dev);
+ ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
- dev_err(codec->dev, "Enabling device failed: %d\n",
+ dev_err(component->dev, "Enabling device failed: %d\n",
ret);
goto probe_fail;
}
- snd_soc_write(codec, TAS2552_CFG_1, TAS2552_MUTE_MASK |
- TAS2552_PLL_SRC_BCLK);
- snd_soc_write(codec, TAS2552_CFG_3, TAS2552_I2S_OUT_SEL |
- TAS2552_DIN_SRC_SEL_AVG_L_R | TAS2552_88_96KHZ);
- snd_soc_write(codec, TAS2552_DOUT, TAS2552_PDM_DATA_I);
- snd_soc_write(codec, TAS2552_OUTPUT_DATA, TAS2552_PDM_DATA_V_I | 0x8);
- snd_soc_write(codec, TAS2552_PDM_CFG, TAS2552_PDM_BCLK_SEL);
- snd_soc_write(codec, TAS2552_BOOST_PT_CTRL, TAS2552_APT_DELAY_200 |
- TAS2552_APT_THRESH_2_1_7);
-
- ret = regmap_register_patch(tas2552->regmap, tas2552_init_regs,
- ARRAY_SIZE(tas2552_init_regs));
- if (ret != 0) {
- dev_err(codec->dev, "Failed to write init registers: %d\n",
- ret);
- goto patch_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_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
- TAS2552_APT_EN | TAS2552_LIM_EN);
+ snd_soc_component_write(component, TAS2552_CFG_2, TAS2552_BOOST_EN | TAS2552_APT_EN |
+ TAS2552_LIM_EN);
return 0;
-patch_fail:
- pm_runtime_put(codec->dev);
probe_fail:
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 0);
+ pm_runtime_put_noidle(component->dev);
+ gpiod_set_value(tas2552->enable_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
- return -EIO;
+ return ret;
}
-static int tas2552_codec_remove(struct snd_soc_codec *codec)
+static void tas2552_component_remove(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ struct tas2552_data *tas2552 = snd_soc_component_get_drvdata(component);
- pm_runtime_put(codec->dev);
+ pm_runtime_put(component->dev);
- if (tas2552->enable_gpio)
- gpiod_set_value(tas2552->enable_gpio, 0);
-
- return 0;
+ gpiod_set_value(tas2552->enable_gpio, 0);
};
#ifdef CONFIG_PM
-static int tas2552_suspend(struct snd_soc_codec *codec)
+static int tas2552_suspend(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to disable supplies: %d\n",
+ dev_err(component->dev, "Failed to disable supplies: %d\n",
ret);
- return 0;
+ return ret;
}
-static int tas2552_resume(struct snd_soc_codec *codec)
+static int tas2552_resume(struct snd_soc_component *component)
{
- struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable supplies: %d\n",
+ dev_err(component->dev, "Failed to enable supplies: %d\n",
ret);
}
- return 0;
+ return ret;
}
#else
#define tas2552_suspend NULL
#define tas2552_resume NULL
#endif
-static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
- .probe = tas2552_codec_probe,
- .remove = tas2552_codec_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),
+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 = {
@@ -472,8 +684,7 @@ static const struct regmap_config tas2552_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static int tas2552_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static int tas2552_probe(struct i2c_client *client)
{
struct device *dev;
struct tas2552_data *data;
@@ -485,16 +696,10 @@ static int tas2552_probe(struct i2c_client *client,
if (data == NULL)
return -ENOMEM;
- data->enable_gpio = devm_gpiod_get(dev, "enable");
- if (IS_ERR(data->enable_gpio)) {
- ret = PTR_ERR(data->enable_gpio);
- if (ret != -ENOENT && ret != -ENOSYS)
- return ret;
-
- data->enable_gpio = NULL;
- } else {
- gpiod_direction_output(data->enable_gpio, 0);
- }
+ 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);
@@ -519,28 +724,28 @@ static int tas2552_probe(struct i2c_client *client,
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 = snd_soc_register_codec(&client->dev,
- &soc_codec_dev_tas2552,
+ 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 codec: %d\n", ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register component: %d\n", ret);
+ pm_runtime_get_noresume(&client->dev);
+ }
return ret;
}
-static int tas2552_i2c_remove(struct i2c_client *client)
+static void tas2552_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ pm_runtime_disable(&client->dev);
}
static const struct i2c_device_id tas2552_id[] = {
- { "tas2552", 0 },
+ { "tas2552" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2552_id);
@@ -556,9 +761,8 @@ MODULE_DEVICE_TABLE(of, tas2552_of_match);
static struct i2c_driver tas2552_i2c_driver = {
.driver = {
.name = "tas2552",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tas2552_of_match),
- .pm = &tas2552_pm,
+ .pm = pm_ptr(&tas2552_pm),
},
.probe = tas2552_probe,
.remove = tas2552_i2c_remove,
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index 6cea8f31bf88..b9c2e70df57e 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -1,25 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* tas2552.h - ALSA SoC Texas Instruments TAS2552 Mono Audio Amplifier
*
- * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com
*
* Author: Dan Murphy <dmurphy@ti.com>
- *
- * 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.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
*/
#ifndef __TAS2552_H__
#define __TAS2552_H__
/* Register Address Map */
-#define TAS2552_DEVICE_STATUS 0x00
+#define TAS2552_DEVICE_STATUS 0x00
#define TAS2552_CFG_1 0x01
#define TAS2552_CFG_2 0x02
#define TAS2552_CFG_3 0x03
@@ -33,22 +25,26 @@
#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_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_PT_CTRL 0x14
+#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 0x20
+#define TAS2552_MAX_REG TAS2552_VBAT_DATA
/* CFG1 Register Masks */
-#define TAS2552_MUTE_MASK (1 << 2)
-#define TAS2552_SWS_MASK (1 << 1)
-#define TAS2552_WCLK_MASK 0x07
-#define TAS2552_CLASSD_EN_MASK (1 << 7)
+#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)
@@ -59,71 +55,84 @@
#define TAS2552_IVSENSE_EN (1 << 1)
/* CFG3 Register Masks */
-#define TAS2552_WORD_CLK_MASK (1 << 7)
-#define TAS2552_BIT_CLK_MASK (1 << 6)
-#define TAS2552_DATA_FORMAT_MASK (0x11 << 2)
-
-#define TAS2552_DAIFMT_I2S_MASK 0xf3
-#define TAS2552_DAIFMT_DSP (1 << 3)
-#define TAS2552_DAIFMT_RIGHT_J (1 << 4)
-#define TAS2552_DAIFMT_LEFT_J (0x11 << 3)
-
-#define TAS2552_PLL_SRC_MCLK 0x00
-#define TAS2552_PLL_SRC_BCLK (1 << 3)
-#define TAS2552_PLL_SRC_IVCLKIN (1 << 4)
-#define TAS2552_PLL_SRC_1_8_FIXED (0x11 << 3)
-
-#define TAS2552_DIN_SRC_SEL_MUTED 0x00
-#define TAS2552_DIN_SRC_SEL_LEFT (1 << 4)
-#define TAS2552_DIN_SRC_SEL_RIGHT (1 << 5)
-#define TAS2552_DIN_SRC_SEL_AVG_L_R (0x11 << 4)
-
+#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)
-
-/* CFG3 WCLK Dividers */
-#define TAS2552_8KHZ 0x00
-#define TAS2552_11_12KHZ (1 << 1)
-#define TAS2552_16KHZ (1 << 2)
-#define TAS2552_22_24KHZ (1 << 3)
-#define TAS2552_32KHZ (1 << 4)
-#define TAS2552_44_48KHZ (1 << 5)
-#define TAS2552_88_96KHZ (1 << 6)
-#define TAS2552_176_192KHZ (1 << 7)
+#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_PDM_DATA_I 0x00
-#define TAS2552_PDM_DATA_V (1 << 6)
-#define TAS2552_PDM_DATA_I_V (1 << 7)
-#define TAS2552_PDM_DATA_V_I (0x11 << 6)
+#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_DATA_ES_RISE 0x4
-
-#define TAS2552_PDM_PLL_CLK_SEL 0x00
-#define TAS2552_PDM_IV_CLK_SEL (1 << 1)
-#define TAS2552_PDM_BCLK_SEL (1 << 2)
-#define TAS2552_PDM_MCLK_SEL (1 << 3)
-
-/* Boost pass-through register */
-#define TAS2552_APT_DELAY_50 0x00
-#define TAS2552_APT_DELAY_75 (1 << 1)
-#define TAS2552_APT_DELAY_125 (1 << 2)
-#define TAS2552_APT_DELAY_200 (1 << 3)
-
-#define TAS2552_APT_THRESH_2_5 0x00
-#define TAS2552_APT_THRESH_1_7 (1 << 3)
-#define TAS2552_APT_THRESH_1_4_1_1 (1 << 4)
-#define TAS2552_APT_THRESH_2_1_7 (0x11 << 2)
+#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_245MHZ_CLK 24576000
-#define TAS2552_225MHZ_CLK 22579200
-#define TAS2552_PLL_J_MASK 0x7f
-#define TAS2552_PLL_D_UPPER_MASK 0x3f
-#define TAS2552_PLL_D_LOWER_MASK 0xff
-#define TAS2552_PLL_BYPASS_MASK 0x80
-#define TAS2552_PLL_BYPASS 0x80
+#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 000000000000..8e00dcc09d0c
--- /dev/null
+++ b/sound/soc/codecs/tas2562.c
@@ -0,0 +1,781 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2562 CODEC
+// Copyright (C) 2019 Texas Instruments Inc.
+
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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,
+ 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),
+ SOC_SINGLE_EXT_TLV("Digital Volume Control", TAS2562_DVC_CFG1, 0, 110, 0,
+ tas2562_volume_control_get, tas2562_volume_control_put,
+ dvc_tlv),
+};
+
+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 },
+ { "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;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->dev = &client->dev;
+ data->model_id = (uintptr_t)i2c_get_match_data(client);
+
+ 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,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 <dmurphy@ti.com>");
+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 000000000000..55b2a1f52ca3
--- /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 <dmurphy@ti.com>
+ */
+
+#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-quirks.h b/sound/soc/codecs/tas2764-quirks.h
new file mode 100644
index 000000000000..7a62b3ba5b40
--- /dev/null
+++ b/sound/soc/codecs/tas2764-quirks.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __TAS2764_QUIRKS__
+#define __TAS2764_QUIRKS__
+
+#include <linux/regmap.h>
+
+#include "tas2764.h"
+
+/* Bitmask of enabled Apple quirks */
+#define ENABLED_APPLE_QUIRKS 0x3f
+
+/*
+ * Disable noise gate and flip down reserved bit in NS_CFG0
+ */
+#define TAS2764_NOISE_GATE_DISABLE BIT(0)
+
+static const struct reg_sequence tas2764_noise_gate_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0)
+};
+
+/*
+ * CONV_VBAT_PVDD_MODE=1
+ */
+#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1)
+
+static const struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41)
+};
+
+/*
+ * Reset of DAC modulator when DSP is OFF
+ */
+#define TAS2764_DMOD_RST BIT(2)
+
+static const struct reg_sequence tas2764_dmod_rst_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0)
+};
+
+/*
+ * Unknown 0x133/0x137 writes (maybe TDM related)
+ */
+#define TAS2764_UNK_SEQ0 BIT(3)
+
+static const struct reg_sequence tas2764_unk_seq0[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80),
+ REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a),
+};
+
+/*
+ * Unknown 0x614 - 0x61f writes
+ */
+#define TAS2764_APPLE_UNK_SEQ1 BIT(4)
+
+static const struct reg_sequence tas2764_unk_seq1[] = {
+ REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52),
+ REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4),
+ REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0),
+};
+
+/*
+ * Unknown writes in the 0xfd page (with secondary paging inside)
+ */
+#define TAS2764_APPLE_UNK_SEQ2 BIT(5)
+
+static const struct reg_sequence tas2764_unk_seq2[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+/*
+ * Disable 'Thermal Threshold 1'
+ */
+#define TAS2764_THERMAL_TH1_DISABLE BIT(6)
+
+static const struct reg_sequence tas2764_thermal_th1_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2),
+};
+
+/*
+ * Imitate Apple's shutdown dance
+ */
+#define TAS2764_SHUTDOWN_DANCE BIT(7)
+
+static const struct reg_sequence tas2764_shutdown_dance_init_seq[] = {
+ /*
+ * SDZ_MODE=01 (immediate)
+ *
+ * We want the shutdown to happen under the influence of
+ * the magic writes in the 0xfdXX region, so make sure
+ * the shutdown is immediate and there's no grace period
+ * followed by the codec part.
+ */
+ REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60),
+};
+
+static const struct reg_sequence tas2764_pre_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */
+};
+
+static const struct reg_sequence tas2764_post_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764,
+ unsigned int target)
+{
+ unsigned int curr;
+ int ret;
+
+ curr = snd_soc_component_read_field(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK);
+
+ if (target == curr)
+ return 0;
+
+ /* Handle power state transition to shutdown */
+ if (target == TAS2764_PWR_CTRL_SHUTDOWN &&
+ (curr == TAS2764_PWR_CTRL_MUTE || curr == TAS2764_PWR_CTRL_ACTIVE)) {
+ ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq,
+ ARRAY_SIZE(tas2764_pre_shutdown_seq));
+ if (!ret)
+ ret = snd_soc_component_update_bits(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK,
+ TAS2764_PWR_CTRL_SHUTDOWN);
+ if (!ret)
+ ret = regmap_multi_reg_write(tas2764->regmap,
+ tas2764_post_shutdown_seq,
+ ARRAY_SIZE(tas2764_post_shutdown_seq));
+ }
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK, target);
+
+ return ret;
+}
+
+/*
+ * Via devicetree (TODO):
+ * - switch from spread spectrum to class-D switching
+ * - disable edge control
+ * - set BOP settings (the BOP config bits *and* BOP_SRC)
+ */
+
+/*
+ * Other setup TODOs:
+ * - DVC ramp rate
+ */
+
+static const struct tas2764_quirk_init_sequence {
+ const struct reg_sequence *seq;
+ int len;
+} tas2764_quirk_init_sequences[] = {
+ { tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) },
+ { tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) },
+ { tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) },
+ { tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) },
+ { tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) },
+ { tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) },
+ { tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) },
+ { tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) },
+};
+
+#endif /* __TAS2764_QUIRKS__ */
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
new file mode 100644
index 000000000000..36e25e48b354
--- /dev/null
+++ b/sound/soc/codecs/tas2764.c
@@ -0,0 +1,950 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Driver for the Texas Instruments TAS2764 CODEC
+// Copyright (C) 2020 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tas2764.h"
+
+enum tas2764_devid {
+ DEVID_TAS2764 = 0,
+ DEVID_SN012776 = 1
+};
+
+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;
+ enum tas2764_devid devid;
+
+ int v_sense_slot;
+ int i_sense_slot;
+
+ bool dac_powered;
+ bool unmuted;
+};
+
+#include "tas2764-quirks.h"
+
+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;
+
+ if (ENABLED_APPLE_QUIRKS & TAS2764_SHUTDOWN_DANCE)
+ return tas2764_do_quirky_pwr_ctrl_change(tas2764, val);
+
+ 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);
+
+ usleep_range(6000, 7000);
+
+ 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 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("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ 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);
+ int ret;
+
+ if (!mute) {
+ tas2764->dac_powered = true;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+ }
+
+ tas2764->unmuted = !mute;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+
+ if (mute) {
+ /* Wait for ramp-down */
+ usleep_range(6000, 7000);
+
+ tas2764->dac_powered = false;
+ ret = tas2764_update_pwr_ctrl(tas2764);
+ if (ret)
+ return ret;
+
+ /* Wait a bit after shutdown */
+ usleep_range(2000, 3000);
+ }
+
+ return 0;
+}
+
+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, asi_cfg_4 = 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;
+ asi_cfg_4 = TAS2764_TDM_CFG4_TX_FALLING;
+ 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;
+ asi_cfg_4 = TAS2764_TDM_CFG4_TX_RISING;
+ break;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+ TAS2764_TDM_CFG1_RX_MASK,
+ asi_cfg_1);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG4,
+ TAS2764_TDM_CFG4_TX_MASK,
+ asi_cfg_4);
+ 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 uint8_t sn012776_bop_presets[] = {
+ 0x01, 0x32, 0x02, 0x22, 0x83, 0x2d, 0x80, 0x02, 0x06,
+ 0x32, 0x46, 0x30, 0x02, 0x06, 0x38, 0x40, 0x30, 0x02,
+ 0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6
+};
+
+static const struct regmap_config tas2764_i2c_regmap;
+
+static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) {
+ const struct tas2764_quirk_init_sequence *init_seq =
+ &tas2764_quirk_init_sequences[i];
+
+ if (!init_seq->seq)
+ continue;
+
+ if (!(BIT(i) & ENABLED_APPLE_QUIRKS))
+ continue;
+
+ ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq,
+ init_seq->len);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result)
+{
+ int ret, reg;
+
+ ret = regmap_read(tas2764->regmap, TAS2764_TEMP, &reg);
+ if (ret)
+ return ret;
+ /*
+ * As per datasheet, subtract 93 from raw value to get degrees
+ * Celsius. hwmon wants millidegrees.
+ *
+ * NOTE: The chip will initialise the TAS2764_TEMP register to
+ * 2.6 *C to avoid triggering temperature protection. Since the
+ * ADC is powered down during software shutdown, this value will
+ * persist until the chip is fully powered up (e.g. the PCM it's
+ * attached to is opened). The ADC will power down again when
+ * the chip is put back into software shutdown, with the last
+ * value sampled persisting in the ADC's register.
+ */
+ *result = (reg - 93) * 1000;
+ return 0;
+}
+
+static umode_t tas2764_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2764_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2764_priv *tas2764 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2764_read_die_temp(tas2764, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2764_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2764_hwmon_ops = {
+ .is_visible = tas2764_hwmon_is_visible,
+ .read = tas2764_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2764_hwmon_chip_info = {
+ .ops = &tas2764_hwmon_ops,
+ .info = tas2764_hwmon_info,
+};
+
+static int tas2764_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
+ int ret, i;
+
+ tas2764->component = component;
+
+ if (tas2764->sdz_gpio) {
+ gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
+
+ tas2764_reset(tas2764);
+ regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap);
+
+ if (tas2764->irq) {
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00);
+ 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;
+
+ switch (tas2764->devid) {
+ case DEVID_SN012776:
+ ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_BOP_SRC,
+ TAS2764_PWR_CTRL_BOP_SRC);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(sn012776_bop_presets); i++) {
+ ret = snd_soc_component_write(component,
+ TAS2764_BOP_CFG0 + i,
+ sn012776_bop_presets[i]);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Apply all enabled Apple quirks */
+ ret = tas2764_apply_init_quirks(tas2764);
+
+ if (ret < 0)
+ return ret;
+
+ break;
+ default:
+ break;
+ }
+
+ 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 char * const tas2764_oce_texts[] = {
+ "Disable", "Retry",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2764_oce_enum, TAS2764_MISC_CFG1,
+ TAS2764_MISC_CFG1_OCE_RETRY_SHIFT, tas2764_oce_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),
+ SOC_ENUM("OCE Handling", tas2764_oce_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 },
+ { TAS2764_INT_CLK_CFG, 0x19 },
+};
+
+static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 0xffff,
+ .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_SW_RST:
+ case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:
+ case TAS2764_INT_CLK_CFG:
+ return true;
+ case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0):
+ /* TI's undocumented registers for the application of quirks */
+ 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 = 0xffff,
+};
+
+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->devid = (kernel_ulong_t)of_device_get_match_data(&client->dev);
+
+ 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;
+ }
+ }
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764",
+ tas2764,
+ &tas2764_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
+
+ 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"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2764_of_match[] = {
+ { .compatible = "ti,tas2764", .data = (void *)DEVID_TAS2764 },
+ { .compatible = "ti,sn012776", .data = (void *)DEVID_SN012776 },
+ {},
+};
+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 <dmurphy@ti.com>");
+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 000000000000..538290ed3d92
--- /dev/null
+++ b/sound/soc/codecs/tas2764.h
@@ -0,0 +1,129 @@
+/* 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 <dmurphy@ti.com>
+ */
+
+#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(2, 0)
+#define TAS2764_PWR_CTRL_ACTIVE 0x0
+#define TAS2764_PWR_CTRL_MUTE BIT(0)
+#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1)
+#define TAS2764_PWR_CTRL_BOP_SRC BIT(7)
+
+#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)
+
+/* Miscellaneous */
+#define TAS2764_MISC_CFG1 TAS2764_REG(0x0, 0x06)
+#define TAS2764_MISC_CFG1_OCE_RETRY_SHIFT 5
+
+/* 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 Reg4 */
+#define TAS2764_TDM_CFG4 TAS2764_REG(0X0, 0x0d)
+#define TAS2764_TDM_CFG4_TX_MASK BIT(0)
+#define TAS2764_TDM_CFG4_TX_RISING 0x0
+#define TAS2764_TDM_CFG4_TX_FALLING BIT(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)
+
+/* Readout Registers */
+#define TAS2764_TEMP TAS2764_REG(0x0, 0x56)
+
+/* Clock/IRQ Settings */
+#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)
+#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2)
+
+#define TAS2764_BOP_CFG0 TAS2764_REG(0X0, 0x1d)
+
+#endif /* __TAS2764__ */
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
new file mode 100644
index 000000000000..6f878b01716f
--- /dev/null
+++ b/sound/soc/codecs/tas2770.c
@@ -0,0 +1,890 @@
+// 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 <tracy-yi@ti.com>
+// Frank Shi <shifu0704@thundersoft.com>
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#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 int sense_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);
+
+ /*
+ * Powering up ISENSE/VSENSE requires a trip through the shutdown state.
+ * Do that here to ensure that our changes are applied properly, otherwise
+ * we might end up with non-functional IVSENSE if playback started earlier,
+ * which would break software speaker protection.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_REG:
+ return snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ case SND_SOC_DAPM_POST_REG:
+ return tas2770_update_pwr_ctrl(tas2770);
+ default:
+ return 0;
+ }
+}
+
+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_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
+ 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_ivsense_transmit(struct tas2770_priv *tas2770,
+ int i_slot, int v_slot)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int 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 |
+ v_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 |
+ i_slot);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int tas2770_set_pdm_transmit(struct tas2770_priv *tas2770, int slot)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG7,
+ TAS2770_TDM_CFG_REG7_PDM_MASK |
+ TAS2770_TDM_CFG_REG7_50_MASK,
+ TAS2770_TDM_CFG_REG7_PDM_ENABLE |
+ slot);
+ return ret;
+}
+
+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);
+ 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);
+ 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);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ 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 int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result)
+{
+ int ret = 0;
+ int reading, msb, lsb;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_MSB, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_LSB, &lsb);
+ if (ret)
+ return ret;
+
+ reading = (msb << 4) | (lsb >> 4);
+
+ /*
+ * As per datasheet: divide register by 16 and subtract 93 to get
+ * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding
+ * errors by subtracting 93 * 16 then multiplying by 1000 / 16.
+ *
+ * NOTE: The ADC registers are initialised to 0 on reset. This means
+ * that the temperature will read -93 *C until the chip is brought out
+ * of software shutdown (e.g. the PCM it's attached to is opened). The
+ * ADC is also shut down in software shutdown/low-power mode, so the
+ * value read back from its registers will be the last value sampled
+ * before entering software shutdown.
+ */
+ *result = (reading - (93 * 16)) * (1000 / 16);
+ return 0;
+}
+
+static umode_t tas2770_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2770_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2770_priv *tas2770 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2770_read_die_temp(tas2770, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2770_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2770_hwmon_ops = {
+ .is_visible = tas2770_hwmon_is_visible,
+ .read = tas2770_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2770_hwmon_chip_info = {
+ .ops = &tas2770_hwmon_ops,
+ .info = tas2770_hwmon_info,
+};
+
+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);
+ int ret;
+
+ 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);
+
+ if (tas2770->i_sense_slot != -1 && tas2770->v_sense_slot != -1) {
+ ret = tas2770_set_ivsense_transmit(tas2770, tas2770->i_sense_slot,
+ tas2770->v_sense_slot);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ if (tas2770->pdm_slot != -1) {
+ ret = tas2770_set_pdm_transmit(tas2770, tas2770->pdm_slot);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2770_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -10050, 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 = -1;
+ }
+
+ 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 = -1;
+ }
+
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,pdm-slot-no",
+ &tas2770->pdm_slot);
+ if (rc)
+ tas2770->pdm_slot = -1;
+
+ 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;
+ }
+ }
+
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2770",
+ tas2770,
+ &tas2770_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
+ 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"},
+ { }
+};
+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 <shifu0704@thundersoft.com>");
+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 000000000000..3fd2e7003c50
--- /dev/null
+++ b/sound/soc/codecs/tas2770.h
@@ -0,0 +1,151 @@
+/* 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)
+ /* TDM Configuration Reg10 */
+#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11)
+#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG7_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;
+ int pdm_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 000000000000..a1963415c931
--- /dev/null
+++ b/sound/soc/codecs/tas2780.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0
+// Driver for the Texas Instruments TAS2780 Mono
+// Audio amplifier
+// Copyright (C) 2022 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#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;
+
+ 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;
+ }
+ 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"},
+ { }
+};
+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 <raphael-xu@ti.com>");
+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 000000000000..661c25df4e29
--- /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 <raphael-xu@ti.com>
+ */
+
+#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-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c
new file mode 100644
index 000000000000..b3fd7350143b
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib-i2c.c
@@ -0,0 +1,371 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C
+//
+// Copyright 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+
+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 channel:%d\n",
+ __func__, ret, chn);
+ 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 tasdev_chn_switch(struct tasdevice_priv *tas_priv,
+ unsigned short chn)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ /* All devices share the same regmap, clear the page
+ * inside regmap once switching to another device.
+ * 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);
+ return ret;
+ }
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tasdev_chn_switch);
+
+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 = tas_priv->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);
+
+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;
+ tas_priv->isspi = false;
+
+ 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;
+ }
+
+ tas_priv->update_bits = tasdevice_dev_update_bits;
+ tas_priv->change_chn_book = tasdevice_change_chn_book;
+ tas_priv->dev_read = tasdevice_dev_read;
+ tas_priv->dev_bulk_read = tasdevice_dev_bulk_read;
+
+ mutex_init(&tas_priv->codec_lock);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_init);
+
+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_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);
+
+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);
+
+void tasdevice_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,
+ TASDEVICE_REG_SWRESET,
+ tas_dev->chip_id >= TAS5825 ?
+ TAS5825_REG_SWRESET_RESET :
+ TASDEVICE_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(tasdevice_reset);
+
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ struct module *module,
+ 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);
+
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
+ tas_priv->name_prefix, tas_priv->dev_name,
+ tas_priv->ndev);
+ else
+ 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(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);
+
+MODULE_DESCRIPTION("TAS2781 common library for I2C");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
new file mode 100644
index 000000000000..4cec9f8a00af
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/dev_printk.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/tas2781.h>
+
+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 = tas_priv->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_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 = tas_priv->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_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 = tas_priv->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 = tas_priv->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);
+
+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)
+{
+ mutex_destroy(&tas_priv->codec_lock);
+}
+EXPORT_SYMBOL_GPL(tasdevice_remove);
+
+MODULE_DESCRIPTION("TAS2781 common library");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
new file mode 100644
index 000000000000..78fd0a5dc6f2
--- /dev/null
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -0,0 +1,2618 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// tas2781-fmwlib.c -- TASDEVICE firmware support
+//
+// Copyright 2023 - 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Baojun Xu <baojun.xu@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781.h>
+#include <linux/unaligned.h>
+
+#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_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
+#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;
+};
+
+struct blktyp_devidx_map {
+ unsigned char blktyp;
+ unsigned char dev_idx;
+};
+
+static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4, 1, 2
+};
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static const struct blktyp_devidx_map ppc3_tas2781_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { PRE_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { POST_SOFTWARE_RESET_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { PRE_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { POST_SOFTWARE_RESET_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { PRE_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { POST_SOFTWARE_RESET_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+ { PRE_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+ { POST_SOFTWARE_RESET_DEVICE_D, 0xC4 },
+};
+
+static const struct blktyp_devidx_map ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES_1X, 0x80 },
+ { MAIN_DEVICE_A_1X, 0x81 },
+ { COEFF_DEVICE_A_1X, 0xC1 },
+ { PRE_DEVICE_A_1X, 0xC1 },
+ { MAIN_DEVICE_B_1X, 0x82 },
+ { COEFF_DEVICE_B_1X, 0xC2 },
+ { PRE_DEVICE_B_1X, 0xC2 },
+ { MAIN_DEVICE_C_1X, 0x83 },
+ { COEFF_DEVICE_C_1X, 0xC3 },
+ { PRE_DEVICE_C_1X, 0xC3 },
+ { MAIN_DEVICE_D_1X, 0x84 },
+ { COEFF_DEVICE_D_1X, 0xC4 },
+ { PRE_DEVICE_D_1X, 0xC4 },
+};
+
+static const struct blktyp_devidx_map non_ppc3_mapping_table[] = {
+ { MAIN_ALL_DEVICES, 0x80 },
+ { MAIN_DEVICE_A, 0x81 },
+ { COEFF_DEVICE_A, 0xC1 },
+ { PRE_DEVICE_A, 0xC1 },
+ { MAIN_DEVICE_B, 0x82 },
+ { COEFF_DEVICE_B, 0xC2 },
+ { PRE_DEVICE_B, 0xC2 },
+ { MAIN_DEVICE_C, 0x83 },
+ { COEFF_DEVICE_C, 0xC3 },
+ { PRE_DEVICE_C, 0xC3 },
+ { MAIN_DEVICE_D, 0x84 },
+ { COEFF_DEVICE_D, 0xC4 },
+ { PRE_DEVICE_D, 0xC4 },
+};
+
+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;
+ }
+ /* If in the RCA bin file are several profiles with the
+ * keyword "init", init_profile_id only store the last
+ * init profile id.
+ */
+ if (strnstr(&config_data[config_offset], "init", 64)) {
+ tas_priv->rcabin.init_profile_id =
+ tas_priv->rcabin.ncfgs - 1;
+ dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
+ __func__, tas_priv->rcabin.init_profile_id);
+ }
+ 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 = get_unaligned_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 =
+ get_unaligned_be16(&config_data[config_offset]);
+ config_offset += 2;
+ bk_da[i]->block_size =
+ get_unaligned_be32(&config_data[config_offset]);
+ config_offset += 4;
+
+ bk_da[i]->n_subblks =
+ get_unaligned_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);
+ /* Initialize to none */
+ rca->init_profile_id = -1;
+ 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 = get_unaligned_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 = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ fw_hdr->binary_version_num = get_unaligned_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 = get_unaligned_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 = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+
+ for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) {
+ fw_hdr->config_size[i] = get_unaligned_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");
+
+/* fixed m68k compiling issue: mapping table can save code field */
+static unsigned char map_dev_idx(struct tasdevice_fw *tas_fmw,
+ struct tasdev_blk *block)
+{
+
+ struct blktyp_devidx_map *p =
+ (struct blktyp_devidx_map *)non_ppc3_mapping_table;
+ struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr);
+ struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr);
+
+ int i, n = ARRAY_SIZE(non_ppc3_mapping_table);
+ unsigned char dev_idx = 0;
+
+ if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN) {
+ p = (struct blktyp_devidx_map *)ppc3_tas2781_mapping_table;
+ n = ARRAY_SIZE(ppc3_tas2781_mapping_table);
+ } else if (fw_fixed_hdr->ppcver >= PPC3_VERSION_BASE) {
+ p = (struct blktyp_devidx_map *)ppc3_mapping_table;
+ n = ARRAY_SIZE(ppc3_mapping_table);
+ }
+
+ for (i = 0; i < n; i++) {
+ if (block->type == p[i].blktyp) {
+ dev_idx = p[i].dev_idx;
+ break;
+ }
+ }
+
+ return dev_idx;
+}
+
+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 = get_unaligned_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 = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ block->nr_subblocks = get_unaligned_be32(&data[offset]);
+ offset += 4;
+
+ /* fixed m68k compiling issue:
+ * 1. mapping table can save code field.
+ * 2. storing the dev_idx as a member of block can reduce unnecessary
+ * time and system resource comsumption of dev_idx mapping every
+ * time the block data writing to the dsp.
+ */
+ block->dev_idx = map_dev_idx(tas_fmw, block);
+
+ 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 = get_unaligned_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_tas5825_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__);
+ return -EINVAL;
+ }
+ /* Skip 65 unused byts*/
+ offset += 65;
+ offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return offset;
+}
+
+static int fw_parse_tas5825_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__);
+ return -EINVAL;
+ }
+ memcpy(config->name, &data[offset], 64);
+ /* Skip extra 8 bytes*/
+ offset += 72;
+ offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data),
+ fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ 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 void fct_param_address_parser(struct cali_reg *r,
+ struct tasdevice_fw *tas_fmw, const unsigned char *data)
+{
+ struct fct_param_address *p = &tas_fmw->fct_par_addr;
+ unsigned int i;
+
+ /*
+ * Calibration parameters locations and data schema in dsp firmware.
+ * The number of items are flexible, but not more than 20. The dsp tool
+ * will reseve 20*24-byte space for fct params. In some cases, the
+ * number of fct param is less than 20, the data will be saved from the
+ * beginning, the rest part will be stuffed with zero.
+ *
+ * fct_param_num (not more than 20)
+ * for (i = 0; i < fct_param_num; i++) {
+ * Alias of fct param (20 bytes)
+ * Book (1 byte)
+ * Page (1 byte)
+ * Offset (1 byte)
+ * CoeffLength (1 byte) = 0x1
+ * }
+ * if (20 - fct_param_num)
+ * 24*(20 - fct_param_num) pieces of '0' as stuffing
+ *
+ * As follow:
+ * umg_SsmKEGCye = Book, Page, Offset, CoeffLength
+ * iks_E0 = Book, Page, Offset, CoeffLength
+ * yep_LsqM0 = Book, Page, Offset, CoeffLength
+ * oyz_U0_ujx = Book, Page, Offset, CoeffLength
+ * iks_GC_GMgq = Book, Page, Offset, CoeffLength
+ * gou_Yao = Book, Page, Offset, CoeffLength
+ * kgd_Wsc_Qsbp = Book, Page, Offset, CoeffLength
+ * yec_CqseSsqs = Book, Page, Offset, CoeffLength
+ * iks_SogkGgog2 = Book, Page, Offset, CoeffLength
+ * yec_Sae_Y = Book, Page, Offset, CoeffLength
+ * Re_Int = Book, Page, Offset, CoeffLength
+ * SigFlag = Book, Page, Offset, CoeffLength
+ * a1_Int = Book, Page, Offset, CoeffLength
+ * a2_Int = Book, Page, Offset, CoeffLength
+ */
+ for (i = 0; i < 20; i++) {
+ const unsigned char *dat = &data[24 * i];
+
+ /*
+ * check whether current fct param is empty.
+ */
+ if (dat[23] != 1)
+ break;
+
+ if (!strncmp(dat, "umg_SsmKEGCye", 20))
+ r->pow_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* high 32-bit of real-time spk impedance */
+ else if (!strncmp(dat, "iks_E0", 20))
+ r->r0_reg = TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* inverse of real-time spk impedance */
+ else if (!strncmp(dat, "yep_LsqM0", 20))
+ r->invr0_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* low 32-bit of real-time spk impedance */
+ else if (!strncmp(dat, "oyz_U0_ujx", 20))
+ r->r0_low_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* Delta Thermal Limit */
+ else if (!strncmp(dat, "iks_GC_GMgq", 20))
+ r->tlimit_reg =
+ TASDEVICE_REG(dat[20], dat[21], dat[22]);
+ /* Thermal data for PG 1.0 device */
+ else if (!strncmp(dat, "gou_Yao", 20))
+ memcpy(p->thr, &dat[20], 3);
+ /* Pilot tone enable flag, usually the sine wave */
+ else if (!strncmp(dat, "kgd_Wsc_Qsbp", 20))
+ memcpy(p->plt_flg, &dat[20], 3);
+ /* Pilot tone gain for calibration */
+ else if (!strncmp(dat, "yec_CqseSsqs", 20))
+ memcpy(p->sin_gn, &dat[20], 3);
+ /* Pilot tone gain for calibration, useless in PG 2.0 */
+ else if (!strncmp(dat, "iks_SogkGgog2", 20))
+ memcpy(p->sin_gn2, &dat[20], 3);
+ /* Thermal data for PG 2.0 device */
+ else if (!strncmp(dat, "yec_Sae_Y", 20))
+ memcpy(p->thr2, &dat[20], 3);
+ /* Spk Equivalent Resistance in fixed-point format */
+ else if (!strncmp(dat, "Re_Int", 20))
+ memcpy(p->r0_reg, &dat[20], 3);
+ /* Check whether the spk connection is open */
+ else if (!strncmp(dat, "SigFlag", 20))
+ memcpy(p->tf_reg, &dat[20], 3);
+ /* check spk resonant frequency */
+ else if (!strncmp(dat, "a1_Int", 20))
+ memcpy(p->a1_reg, &dat[20], 3);
+ /* check spk resonant frequency */
+ else if (!strncmp(dat, "a2_Int", 20))
+ memcpy(p->a2_reg, &dat[20], 3);
+ }
+}
+
+static int fw_parse_fct_param_address(struct tasdevice_priv *tas_priv,
+ struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset)
+{
+ struct calidata *cali_data = &tas_priv->cali_data;
+ struct cali_reg *r = &cali_data->cali_reg_array;
+ const unsigned char *data = fmw->data;
+
+ if (offset + 520 > fmw->size) {
+ dev_err(tas_priv->dev, "%s: File Size error\n", __func__);
+ return -EINVAL;
+ }
+
+ /* skip reserved part */
+ offset += 40;
+
+ fct_param_address_parser(r, tas_fmw, &data[offset]);
+
+ offset += 480;
+
+ 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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_be32(&buf[offset]);
+ offset += 4;
+ }
+
+ /* Skip the unused prog_size */
+ offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs);
+
+ tas_fmw->nr_configurations = get_unaligned_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 = get_unaligned_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 {
+ if (tas_priv->isspi) {
+ chn = tas_priv->index;
+ chnend = chn + 1;
+ } 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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = tas_priv->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)
+{
+ const unsigned int blk_size = block->blk_size;
+ unsigned int i, length;
+ unsigned char *data = block->data;
+
+ for (i = 0, length = 0; i < block->nr_subblocks; i++) {
+ int rc = tasdevice_process_block(tasdevice, data + length,
+ block->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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = get_unaligned_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 = tas_priv->dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
+ &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;
+ 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_CHECKSUM_REG, 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 void dspbin_type_check(struct tasdevice_priv *tas_priv,
+ unsigned int ppcver)
+{
+ if (ppcver >= PPC3_VERSION_TAS2781_ALPHA_MIN) {
+ if (ppcver >= PPC3_VERSION_TAS2781_BETA_MIN)
+ tas_priv->dspbin_typ = TASDEV_BETA;
+ else if (ppcver >= PPC3_VERSION_TAS2781_BASIC_MIN)
+ tas_priv->dspbin_typ = TASDEV_BASIC;
+ else
+ tas_priv->dspbin_typ = TASDEV_ALPHA;
+ }
+ if ((tas_priv->dspbin_typ != TASDEV_BASIC) &&
+ (ppcver < PPC3_VERSION_TAS5825_BASE))
+ tas_priv->fw_parse_fct_param_address =
+ fw_parse_fct_param_address;
+}
+
+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_TAS5825_BASE) {
+ tas_priv->fw_parse_variable_header =
+ fw_parse_variable_header_kernel;
+ tas_priv->fw_parse_program_data =
+ fw_parse_tas5825_program_data_kernel;
+ tas_priv->fw_parse_configuration_data =
+ fw_parse_tas5825_configuration_data_kernel;
+ tas_priv->tasdevice_load_block =
+ tasdevice_load_block_kernel;
+ dspbin_type_check(tas_priv, ppcver);
+ } else if (ppcver >= PPC3_VERSION_BASE) {
+ 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;
+ dspbin_type_check(tas_priv, ppcver);
+ } 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_BASE);
+ 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);
+ static 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 = get_unaligned_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 = get_unaligned_be32(&buf[offset]);
+ offset += 8;
+ fw_fixed_hdr->drv_ver = get_unaligned_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 = get_unaligned_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 = NULL;
+ 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:
+ 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;
+
+ if (!fmw || !fmw->data) {
+ dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n",
+ __func__, tas_priv->coef_binaryname);
+ return -EINVAL;
+ }
+
+ tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL);
+ if (!tas_priv->fmw)
+ return -ENOMEM;
+
+ tas_fmw = tas_priv->fmw;
+ tas_fmw->dev = tas_priv->dev;
+ offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset);
+
+ if (offset == -EINVAL)
+ return -EINVAL;
+
+ 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:
+ case 0x401:
+ 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)
+ return ret;
+ break;
+ }
+
+ offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw,
+ offset);
+ if (offset < 0)
+ return offset;
+
+ offset = tas_priv->fw_parse_configuration_data(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ return offset;
+
+ if (tas_priv->fw_parse_fct_param_address) {
+ offset = tas_priv->fw_parse_fct_param_address(tas_priv,
+ tas_fmw, fmw, offset);
+ if (offset < 0)
+ return offset;
+ }
+
+ return 0;
+}
+
+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;
+}
+
+static void tasdev_load_calibrated_data(struct tasdevice_priv *priv, int i)
+{
+ struct tasdevice_fw *cal_fmw = priv->tasdevice[i].cali_data_fmw;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *data = cali_data->data;
+ struct tasdevice_calibration *cal;
+ int k = i * (cali_data->cali_dat_sz_per_dev + 1);
+ int rc;
+
+ /* Load the calibrated data from cal bin file */
+ if (!priv->is_user_space_calidata && cal_fmw) {
+ cal = cal_fmw->calibrations;
+
+ if (cal)
+ load_calib_data(priv, &cal->dev_data);
+ return;
+ }
+ if (!priv->is_user_space_calidata)
+ return;
+ /* load calibrated data from user space */
+ if (data[k] != i) {
+ dev_err(priv->dev, "%s: no cal-data for dev %d from usr-spc\n",
+ __func__, i);
+ return;
+ }
+ k++;
+
+ rc = tasdevice_dev_bulk_write(priv, i, p->r0_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_reg bulk_wr err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->r0_low_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_low_reg err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->invr0_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d invr0_reg err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->pow_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d pow_reg bulk_wr err = %d\n", i, rc);
+ return;
+ }
+ k += 4;
+ rc = tasdevice_dev_bulk_write(priv, i, p->tlimit_reg, &(data[k]), 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d tlimit_reg err = %d\n", i, rc);
+ return;
+ }
+}
+
+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;
+ if (tas_priv->tasdevice[i].is_loaderr == false &&
+ tas_priv->tasdevice[i].is_loading == true)
+ 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 |= BIT(i + 4);
+ continue;
+ }
+
+ if (tas_priv->tasdevice[i].is_loaderr == false &&
+ tas_priv->tasdevice[i].is_loading == true) {
+ tasdev_load_calibrated_data(tas_priv, i);
+ 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");
+
+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;
+
+ /*
+ * Only RCA-based Playback can still work with no dsp program running
+ * inside the chip.
+ */
+ switch (tas_priv->fw_state) {
+ case TASDEVICE_RCA_FW_OK:
+ case TASDEVICE_DSP_FW_ALL_OK:
+ break;
+ default:
+ return;
+ }
+
+ if (state == 0) {
+ if (tas_fmw && 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, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
new file mode 100644
index 000000000000..1539b70881d1
--- /dev/null
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -0,0 +1,2084 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2563/TAS2781 Audio Smart Amplifier
+//
+// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2563/TAS2781 driver implements a flexible and configurable
+// algo coefficient setting for one, two, or even multiple
+// TAS2563/TAS2781 chips.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+//
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+#include <sound/tlv.h>
+#include <sound/tas2x20-tlv.h>
+#include <sound/tas2563-tlv.h>
+#include <sound/tas2781-tlv.h>
+#include <sound/tas5825-tlv.h>
+#include <linux/unaligned.h>
+
+#define X2563_CL_STT_VAL(xreg, xval) \
+{ .reg = xreg, \
+ .val = { xval }, \
+ .val_len = 1, }
+
+#define X2563_CL_STT_4BYTS(xreg, byte0, byte1, byte2, byte3) \
+{ .reg = xreg, \
+ .val = { byte0, byte1, byte2, byte3 }, \
+ .val_len = 4, }
+
+static const struct bulk_reg_val tas2563_cali_start_reg[] = {
+ X2563_CL_STT_VAL(TAS2563_IDLE, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_ENFF_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_DISTCK_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_TE_SCTHR_REG, 0x7f, 0xff, 0xff, 0xff),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_PLT_FLAG_REG, 0x40, 0x00, 0x00, 0x00),
+ X2563_CL_STT_4BYTS(TAS2563_PRM_SINEGAIN_REG, 0x0a, 0x3d, 0x70, 0xa4),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA1_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA1_AT_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_TA2_REG, 0x00, 0x06, 0xd3, 0x72),
+ X2563_CL_STT_4BYTS(TAS2563_TE_AT_REG, 0x00, 0x36, 0x91, 0x5e),
+ X2563_CL_STT_4BYTS(TAS2563_TE_DT_REG, 0x00, 0x36, 0x91, 0x5e),
+};
+
+#define X2781_CL_STT_VAL(xreg, xval, xlocked) \
+{ .reg = xreg, \
+ .val = { xval }, \
+ .val_len = 1, \
+ .is_locked = xlocked, }
+
+#define X2781_CL_STT_4BYTS_UNLOCKED(xreg, byte0, byte1, byte2, byte3) \
+{ .reg = xreg, \
+ .val = { byte0, byte1, byte2, byte3 }, \
+ .val_len = 4, \
+ .is_locked = false, }
+
+#define X2781_CL_STT_LEN_UNLOCKED(xreg) \
+{ .reg = xreg, \
+ .val_len = 4, \
+ .is_locked = false, }
+
+static const struct bulk_reg_val tas2781_cali_start_reg[] = {
+ X2781_CL_STT_VAL(TAS2781_PRM_INT_MASK_REG, 0xfe, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_CLK_CFG_REG, 0xdd, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_RSVD_REG, 0x20, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_TEST_57_REG, 0x14, true),
+ X2781_CL_STT_VAL(TAS2781_PRM_TEST_62_REG, 0x45, true),
+ X2781_CL_STT_VAL(TAS2781_PRM_PVDD_UVLO_REG, 0x03, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_CHNL_0_REG, 0xa8, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_NG_CFG0_REG, 0xb9, false),
+ X2781_CL_STT_VAL(TAS2781_PRM_IDLE_CH_DET_REG, 0x92, false),
+ /*
+ * This register is pilot tone threshold, different with the
+ * calibration tool version, it will be updated in
+ * tas2781_calib_start_put(), set to 1mA.
+ */
+ X2781_CL_STT_4BYTS_UNLOCKED(0, 0x00, 0x00, 0x00, 0x56),
+ X2781_CL_STT_4BYTS_UNLOCKED(TAS2781_PRM_PLT_FLAG_REG,
+ 0x40, 0x00, 0x00, 0x00),
+ X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN_REG),
+ X2781_CL_STT_LEN_UNLOCKED(TAS2781_PRM_SINEGAIN2_REG),
+};
+
+static const struct i2c_device_id tasdevice_id[] = {
+ { "tas2020", TAS2020 },
+ { "tas2118", TAS2118 },
+ { "tas2120", TAS2120 },
+ { "tas2320", TAS2320 },
+ { "tas2563", TAS2563 },
+ { "tas2570", TAS2570 },
+ { "tas2572", TAS2572 },
+ { "tas2781", TAS2781 },
+ { "tas5825", TAS5825 },
+ { "tas5827", TAS5827 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, tasdevice_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id tasdevice_of_match[] = {
+ { .compatible = "ti,tas2020" },
+ { .compatible = "ti,tas2118" },
+ { .compatible = "ti,tas2120" },
+ { .compatible = "ti,tas2320" },
+ { .compatible = "ti,tas2563" },
+ { .compatible = "ti,tas2570" },
+ { .compatible = "ti,tas2572" },
+ { .compatible = "ti,tas2781" },
+ { .compatible = "ti,tas5825" },
+ { .compatible = "ti,tas5827" },
+ {},
+};
+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 tasdev_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 tasdev_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 int tasdev_cali_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned char *data = cali_data->data;
+ unsigned int i = 0;
+ unsigned int j, k;
+ int rc;
+
+ guard(mutex)(&priv->codec_lock);
+ if (!priv->is_user_space_calidata)
+ return -1;
+
+ if (!p->r0_reg)
+ return -1;
+
+ dst[i++] = bytes_ext->max;
+ dst[i++] = 'r';
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->r0_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->r0_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->r0_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->r0_low_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->r0_low_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->r0_low_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->invr0_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->invr0_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->invr0_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->pow_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->pow_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->pow_reg);
+
+ dst[i++] = TASDEVICE_BOOK_ID(p->tlimit_reg);
+ dst[i++] = TASDEVICE_PAGE_ID(p->tlimit_reg);
+ dst[i++] = TASDEVICE_PAGE_REG(p->tlimit_reg);
+
+ for (j = 0, k = 0; j < priv->ndev; j++) {
+ if (j == data[k]) {
+ dst[i++] = j;
+ k++;
+ } else {
+ dev_err(priv->dev, "chn %d device %u not match\n",
+ j, data[k]);
+ k += 21;
+ continue;
+ }
+ rc = tasdevice_dev_bulk_read(priv, j, p->r0_reg, &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_reg bulk_rd err = %d\n",
+ j, rc);
+ i += 20;
+ k += 20;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d r0_data is not same\n", j);
+ k += 4;
+ i += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->r0_low_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d r0_low bulk_rd err = %d\n",
+ j, rc);
+ i += 16;
+ k += 16;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d r0_low is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->invr0_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d invr0 bulk_rd err = %d\n",
+ j, rc);
+ i += 12;
+ k += 12;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d invr0 is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->pow_reg, &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d pow_reg bulk_rd err = %d\n",
+ j, rc);
+ i += 8;
+ k += 8;
+ continue;
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d pow_reg is not same\n", j);
+ i += 4;
+ k += 4;
+ rc = tasdevice_dev_bulk_read(priv, j, p->tlimit_reg,
+ &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev, "chn %d tlimit bulk_rd err = %d\n",
+ j, rc);
+ }
+ rc = memcmp(&dst[i], &data[k], 4);
+ if (rc != 0)
+ dev_dbg(priv->dev, "chn %d tlimit is not same\n", j);
+ i += 4;
+ k += 4;
+ }
+ return 0;
+}
+
+static int calib_data_get(struct tasdevice_priv *tas_priv, int reg,
+ unsigned char *dst)
+{
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int rc = -1;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ /* First byte is the device index. */
+ dst[0] = i;
+ rc = tasdevice_dev_bulk_read(tas_priv, i, reg, &dst[1],
+ 4);
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int partial_cali_data_update(int *reg, int j)
+{
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ return reg[0];
+ case TAS2781_PRM_PLT_FLAG_REG:
+ return reg[1];
+ case TAS2781_PRM_SINEGAIN_REG:
+ return reg[2];
+ case TAS2781_PRM_SINEGAIN2_REG:
+ return reg[3];
+ default:
+ return 0;
+ }
+}
+
+static void sngl_calib_start(struct tasdevice_priv *tas_priv, int i,
+ int *reg, unsigned char *dat)
+{
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ unsigned char val[4];
+ int j, r;
+
+ if (p == NULL)
+ return;
+
+ /* Store the current setting from the chip */
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(tas_priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_read(tas_priv, i, p[j].reg,
+ (int *)&p[j].val[0]);
+ } else {
+ if (!tas_priv->dspbin_typ) {
+ r = partial_cali_data_update(reg, j);
+ if (r)
+ p[j].reg = r;
+ }
+
+ if (p[j].reg)
+ tasdevice_dev_bulk_read(tas_priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, t->val, 4);
+
+ /* Update the setting for calibration */
+ for (j = 0; j < sum - 4; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(tas_priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ tas2781_cali_start_reg[j].val[0]);
+ }
+ }
+
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x21;
+ val[3] = 0x8e;
+ } else {
+ val[0] = tas2781_cali_start_reg[j].val[0];
+ val[1] = tas2781_cali_start_reg[j].val[1];
+ val[2] = tas2781_cali_start_reg[j].val[2];
+ val[3] = tas2781_cali_start_reg[j].val[3];
+ }
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg, val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 1].reg,
+ (unsigned char *)tas2781_cali_start_reg[j + 1].val, 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 2].reg, &dat[1], 4);
+ tasdevice_dev_bulk_write(tas_priv, i, p[j + 3].reg, &dat[5], 4);
+ if (tas_priv->dspbin_typ == TASDEV_ALPHA) {
+ val[0] = 0x00;
+ val[1] = 0x00;
+ val[2] = 0x2a;
+ val[3] = 0x0b;
+
+ tasdevice_dev_bulk_read(tas_priv, i, t->reg, val, 4);
+ }
+}
+
+static int tas2781_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dat = ucontrol->value.bytes.data;
+ int i, reg[4];
+ int j = 0;
+
+ guard(mutex)(&priv->codec_lock);
+ if (priv->chip_id != TAS2781 || bytes_ext->max != dat[0] ||
+ dat[1] != 'r') {
+ dev_err(priv->dev, "%s: package fmt or chipid incorrect\n",
+ __func__);
+ return 0;
+ }
+ j += 2;
+ /* refresh pilot tone and SineGain register */
+ for (i = 0; i < ARRAY_SIZE(reg); i++) {
+ reg[i] = TASDEVICE_REG(dat[j], dat[j + 1], dat[j + 2]);
+ j += 3;
+ }
+
+ for (i = 0; i < priv->ndev; i++) {
+ int k = i * 9 + j;
+
+ if (dat[k] != i) {
+ dev_err(priv->dev, "%s:no cal-setting for dev %d\n",
+ __func__, i);
+ continue;
+ }
+ sngl_calib_start(priv, i, reg, dat + k);
+ }
+ return 1;
+}
+
+static void tas2781_calib_stop_put(struct tasdevice_priv *priv)
+{
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ int i, j;
+
+ for (i = 0; i < priv->ndev; i++) {
+ struct tasdevice *tasdev = priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+ struct bulk_reg_val *t = &tasdev[i].alp_cali_bckp;
+
+ if (p == NULL)
+ continue;
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1) {
+ if (p[j].is_locked)
+ tasdevice_dev_write(priv, i,
+ TAS2781_TEST_UNLOCK_REG,
+ TAS2781_TEST_PAGE_UNLOCK);
+ tasdevice_dev_write(priv, i, p[j].reg,
+ p[j].val[0]);
+ } else {
+ if (!p[j].reg)
+ continue;
+ tasdevice_dev_bulk_write(priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+
+ if (priv->dspbin_typ == TASDEV_ALPHA)
+ tasdevice_dev_bulk_write(priv, i, t->reg, t->val, 4);
+ }
+}
+
+static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct bulk_reg_val *q = (struct bulk_reg_val *)tas2563_cali_start_reg;
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+ int i, j;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->chip_id != TAS2563)
+ return -1;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+
+ if (p == NULL)
+ continue;
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_read(tas_priv,
+ i, p[j].reg,
+ (unsigned int *)&p[j].val[0]);
+ else
+ tasdevice_dev_bulk_read(tas_priv,
+ i, p[j].reg, p[j].val, 4);
+ }
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ q[j].val[0]);
+ else
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+ q[j].val, 4);
+ }
+ }
+
+ return 1;
+}
+
+static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+ const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
+ int i, j;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ struct bulk_reg_val *p = tasdev[i].cali_data_backup;
+
+ if (p == NULL)
+ continue;
+
+ for (j = 0; j < sum; j++) {
+ if (p[j].val_len == 1)
+ tasdevice_dev_write(tas_priv, i, p[j].reg,
+ p[j].val[0]);
+ else
+ tasdevice_dev_bulk_write(tas_priv, i, p[j].reg,
+ p[j].val, 4);
+ }
+ }
+}
+
+static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+
+ guard(mutex)(&priv->codec_lock);
+ if (priv->chip_id == TAS2563)
+ tas2563_calib_stop_put(priv);
+ else
+ tas2781_calib_stop_put(priv);
+
+ return 1;
+}
+
+static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct calidata *cali_data = &priv->cali_data;
+ struct cali_reg *p = &cali_data->cali_reg_array;
+ unsigned char *src = ucontrol->value.bytes.data;
+ unsigned char *dst = cali_data->data;
+ int i = 0;
+ int j;
+
+ guard(mutex)(&priv->codec_lock);
+ if (src[0] != bytes_ext->max || src[1] != 'r') {
+ dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__);
+ return 0;
+ }
+ for (j = 0; j < priv->ndev; j++) {
+ if (src[17 + j * 21] != j) {
+ dev_err(priv->dev, "%s: pkg fmt invalid\n", __func__);
+ return 0;
+ }
+ }
+ i += 2;
+ priv->is_user_space_calidata = true;
+
+ if (priv->dspbin_typ == TASDEV_BASIC) {
+ p->r0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->r0_low_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->invr0_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->pow_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ p->tlimit_reg = TASDEVICE_REG(src[i], src[i + 1], src[i + 2]);
+ i += 3;
+ } else {
+ i += 15;
+ }
+
+ memcpy(dst, &src[i], cali_data->total_sz);
+ return 1;
+}
+
+static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ int i, val, rc = -1;
+
+ dst[0] = bytes_ext->max;
+ guard(mutex)(&tas_priv->codec_lock);
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ /* First byte is the device index. */
+ dst[1] = i;
+ rc = tasdevice_dev_read(tas_priv, i,
+ TAS2781_RUNTIME_LATCH_RE_REG, &val);
+ if (rc < 0)
+ dev_err(tas_priv->dev, "%s, get value error\n",
+ __func__);
+ else
+ dst[2] = val;
+
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int tasdev_tf_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG_TF;
+
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+
+ reg = TAS2781_RUNTIME_RE_REG_TF;
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->tf_reg[0], p->tf_reg[1],
+ p->tf_reg[2]);
+ } else {
+ reg = TAS2563_RUNTIME_RE_REG_TF;
+ }
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_re_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TAS2781_RUNTIME_RE_REG;
+
+ if (tas_priv->chip_id == TAS2781) {
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->r0_reg[0], p->r0_reg[1],
+ p->r0_reg[2]);
+ } else {
+ reg = TAS2563_RUNTIME_RE_REG;
+ }
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_r0_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct calidata *cali_data = &tas_priv->cali_data;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg;
+
+ guard(mutex)(&tas_priv->codec_lock);
+
+ if (tas_priv->chip_id == TAS2563)
+ reg = TAS2563_PRM_R0_REG;
+ else if (cali_data->cali_reg_array.r0_reg)
+ reg = cali_data->cali_reg_array.r0_reg;
+ else
+ return -1;
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_XMA1_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TASDEVICE_XM_A1_REG;
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a1_reg[0], p->a1_reg[1], p->a1_reg[2]);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_XMA2_data_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct tasdevice_fw *tas_fmw = tas_priv->fmw;
+ struct fct_param_address *p = &(tas_fmw->fct_par_addr);
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned char *dst = ucontrol->value.bytes.data;
+ unsigned int reg = TASDEVICE_XM_A2_REG;
+
+ if (tas_priv->dspbin_typ)
+ reg = TASDEVICE_REG(p->a2_reg[0], p->a2_reg[1], p->a2_reg[2]);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ dst[0] = bytes_ext->max;
+ return calib_data_get(tas_priv, reg, &dst[1]);
+}
+
+static int tasdev_nop_get(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int tasdevice_digital_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 *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ unsigned int l = 0, r = mc->max;
+ unsigned int target, ar_mid, mid, ar_l, ar_r;
+ unsigned int reg = mc->reg;
+ unsigned char data[4];
+ int ret;
+
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ target = get_unaligned_be32(&data[0]);
+
+ while (r > 1 + l) {
+ mid = (l + r) / 2;
+ ar_mid = get_unaligned_be32(tas_dev->dvc_tlv_table[mid]);
+ if (target < ar_mid)
+ r = mid;
+ else
+ l = mid;
+ }
+
+ ar_l = get_unaligned_be32(tas_dev->dvc_tlv_table[l]);
+ ar_r = get_unaligned_be32(tas_dev->dvc_tlv_table[r]);
+
+ /* find out the member same as or closer to the current volume */
+ ucontrol->value.integer.value[0] =
+ abs(target - ar_l) <= abs(target - ar_r) ? l : r;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return 0;
+}
+
+static int tasdevice_digital_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 *codec = snd_soc_kcontrol_component(kcontrol);
+ struct tasdevice_priv *tas_dev = snd_soc_component_get_drvdata(codec);
+ int vol = ucontrol->value.integer.value[0];
+ int status = 0, max = mc->max, rc = 1;
+ int i, ret;
+ unsigned int reg = mc->reg;
+ unsigned int volrd, volwr;
+ unsigned char data[4];
+
+ vol = clamp(vol, 0, max);
+ mutex_lock(&tas_dev->codec_lock);
+ /* Read the primary device */
+ ret = tasdevice_dev_bulk_read(tas_dev, 0, reg, data, 4);
+ if (ret) {
+ dev_err(tas_dev->dev, "%s, get AMP vol error\n", __func__);
+ rc = -1;
+ goto out;
+ }
+
+ volrd = get_unaligned_be32(&data[0]);
+ volwr = get_unaligned_be32(tas_dev->dvc_tlv_table[vol]);
+
+ if (volrd == volwr) {
+ rc = 0;
+ goto out;
+ }
+
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_bulk_write(tas_dev, i, reg,
+ (unsigned char *)tas_dev->dvc_tlv_table[vol], 4);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "%s, set digital vol error in dev %d\n",
+ __func__, i);
+ status |= BIT(i);
+ }
+ }
+
+ if (status)
+ rc = -1;
+out:
+ mutex_unlock(&tas_dev->codec_lock);
+ return rc;
+}
+
+static const struct snd_kcontrol_new tasdevice_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tasdev_calib_stop_put),
+ SND_SOC_BYTES_EXT("Amp TF Data", 6, tasdev_tf_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp RE Data", 6, tasdev_re_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp R0 Data", 6, tasdev_r0_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp XMA1 Data", 6, tasdev_XMA1_data_get, NULL),
+ SND_SOC_BYTES_EXT("Amp XMA2 Data", 6, tasdev_XMA2_data_get, NULL),
+};
+
+static const struct snd_kcontrol_new tas2x20_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2X20_AMP_LEVEL,
+ 0, 0, 42, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2x20_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2X20_DVC_LEVEL,
+ 0, 0, ARRAY_SIZE(tas2x20_dvc_table) - 1, 0,
+ tasdevice_digital_gain_get, tasdevice_digital_gain_put,
+ tas2x20_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS2781_AMP_LEVEL,
+ 1, 0, 20, 0, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas2781_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2781_DVC_LVL,
+ 0, 0, 200, 1, tas2781_digital_getvol,
+ tas2781_digital_putvol, tas2781_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas5825_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Volume", TAS5825_AMP_LEVEL,
+ 0, 0, 31, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS5825_DVC_LEVEL,
+ 0, 0, 254, 1, tas2781_amp_getvol,
+ tas2781_amp_putvol, tas5825_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2781_cali_controls[] = {
+ SND_SOC_BYTES_EXT("Amp Latch Data", 3, tas2781_latch_reg_get, NULL),
+};
+
+static const struct snd_kcontrol_new tas2563_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Volume", TAS2563_DVC_LVL, 0,
+ 0, ARRAY_SIZE(tas2563_dvc_table) - 1, 0,
+ tasdevice_digital_gain_get, tasdevice_digital_gain_put,
+ tas2563_dvc_tlv),
+};
+
+static const struct snd_kcontrol_new tas2563_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tas2563_calib_start_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_active_num(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->ndev - 1;
+
+ return 0;
+}
+
+static int tasdevice_info_chip_id(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = TAS2020;
+ uinfo->value.integer.max = TAS_OTHERS;
+
+ return 0;
+}
+
+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_get_chip_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->chip_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_kstrdup(tas_priv->dev, "Speaker Profile Id", GFP_KERNEL);
+ if (!name) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ 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_active_num_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);
+ struct i2c_client *clt = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (clt->addr == tasdev[i].dev_addr) {
+ ucontrol->value.integer.value[0] = i;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+static int tasdevice_active_num_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);
+ int dev_id = ucontrol->value.integer.value[0];
+ int max = tas_priv->ndev - 1;
+
+ dev_id = clamp(dev_id, 0, max);
+
+ guard(mutex)(&tas_priv->codec_lock);
+ return tasdev_chn_switch(tas_priv, dev_id);
+}
+
+static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
+{
+ struct snd_kcontrol_new *dsp_ctrls;
+ char *active_dev_num, *chip_id, *fw_load;
+ char *conf_name, *prog_name;
+ int nr_controls = 5;
+ int mix_index = 0;
+
+ /* 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)
+ return -ENOMEM;
+
+ /* Create mixer items for selecting the active Program and Config */
+ prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id",
+ GFP_KERNEL);
+ if (!prog_name)
+ return -ENOMEM;
+
+ 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++;
+
+ conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id",
+ GFP_KERNEL);
+ if (!conf_name)
+ return -ENOMEM;
+
+ 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++;
+
+ active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num",
+ GFP_KERNEL);
+ if (!active_dev_num)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = active_dev_num;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_active_num;
+ dsp_ctrls[mix_index].get = tasdevice_active_num_get;
+ dsp_ctrls[mix_index].put = tasdevice_active_num_put;
+ mix_index++;
+
+ chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL);
+ if (!chip_id)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = chip_id;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = tasdevice_info_chip_id;
+ dsp_ctrls[mix_index].get = tasdevice_get_chip_id;
+ mix_index++;
+
+ fw_load = devm_kstrdup(tas_priv->dev, "Speaker Force Firmware Load",
+ GFP_KERNEL);
+ if (!fw_load)
+ return -ENOMEM;
+
+ dsp_ctrls[mix_index].name = fw_load;
+ dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ dsp_ctrls[mix_index].info = snd_soc_info_bool_ext;
+ dsp_ctrls[mix_index].put = tasdev_force_fwload_put;
+ dsp_ctrls[mix_index].get = tasdev_force_fwload_get;
+ dsp_ctrls[mix_index].private_value = 0UL;
+ mix_index++;
+
+ return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
+ nr_controls < mix_index ? nr_controls : mix_index);
+}
+
+static void cali_reg_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ const int sum = ARRAY_SIZE(tas2781_cali_start_reg);
+ int reg, j;
+
+ for (j = 0; j < sum; j++) {
+ switch (tas2781_cali_start_reg[j].reg) {
+ case 0:
+ reg = TASDEVICE_REG(t->thr[0], t->thr[1], t->thr[2]);
+ break;
+ case TAS2781_PRM_PLT_FLAG_REG:
+ reg = TASDEVICE_REG(t->plt_flg[0], t->plt_flg[1],
+ t->plt_flg[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ case TAS2781_PRM_SINEGAIN2_REG:
+ reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1],
+ t->sin_gn[2]);
+ break;
+ default:
+ reg = 0;
+ break;
+ }
+ if (reg)
+ p[j].reg = reg;
+ }
+}
+
+static void alpa_cali_update(struct bulk_reg_val *p,
+ struct fct_param_address *t)
+{
+ p->is_locked = false;
+ p->reg = TASDEVICE_REG(t->thr2[0], t->thr2[1], t->thr2[2]);
+ p->val_len = 4;
+}
+
+static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
+{
+ struct calidata *cali_data = &priv->cali_data;
+ struct tasdevice *tasdev = priv->tasdevice;
+ struct tasdevice_fw *fmw = priv->fmw;
+ struct soc_bytes_ext *ext_cali_data;
+ struct snd_kcontrol_new *cali_ctrls;
+ unsigned int nctrls;
+ char *cali_name;
+ int rc, i;
+
+ rc = snd_soc_add_component_controls(priv->codec,
+ tasdevice_cali_controls, ARRAY_SIZE(tasdevice_cali_controls));
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add cali controls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ if (priv->chip_id == TAS2781) {
+ struct fct_param_address *t = &(fmw->fct_par_addr);
+
+ cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
+ nctrls = ARRAY_SIZE(tas2781_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ struct bulk_reg_val *p;
+
+ p = tasdev[i].cali_data_backup =
+ kmemdup(tas2781_cali_start_reg,
+ sizeof(tas2781_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ if (priv->dspbin_typ) {
+ cali_reg_update(p, t);
+ if (priv->dspbin_typ == TASDEV_ALPHA) {
+ p = &tasdev[i].alp_cali_bckp;
+ alpa_cali_update(p, t);
+ }
+ }
+ }
+ } else {
+ cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
+ nctrls = ARRAY_SIZE(tas2563_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ tasdev[i].cali_data_backup =
+ kmemdup(tas2563_cali_start_reg,
+ sizeof(tas2563_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ }
+ }
+
+ rc = snd_soc_add_component_controls(priv->codec, cali_ctrls, nctrls);
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add chip cali ctrls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ /* index for cali_ctrls */
+ i = 0;
+ if (priv->chip_id == TAS2781)
+ nctrls = 2;
+ else
+ nctrls = 1;
+
+ /*
+ * Alloc kcontrol via devm_kzalloc(), which don't manually
+ * free the kcontrol。
+ */
+ cali_ctrls = devm_kcalloc(priv->dev, nctrls,
+ sizeof(cali_ctrls[0]), GFP_KERNEL);
+ if (!cali_ctrls)
+ return -ENOMEM;
+
+ ext_cali_data = devm_kzalloc(priv->dev, sizeof(*ext_cali_data),
+ GFP_KERNEL);
+ if (!ext_cali_data)
+ return -ENOMEM;
+
+ cali_name = devm_kstrdup(priv->dev, "Speaker Calibrated Data",
+ GFP_KERNEL);
+ if (!cali_name)
+ return -ENOMEM;
+ /* the number of calibrated data per tas2563/tas2781 */
+ cali_data->cali_dat_sz_per_dev = 20;
+ /*
+ * Data structure for tas2563/tas2781 calibrated data:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register array for calibrated data (15 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Calibrated data for Device #i (20 bytes)
+ * }
+ */
+ ext_cali_data->max = priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1) + 1 + 15 + 1;
+ priv->cali_data.total_sz = priv->ndev *
+ (cali_data->cali_dat_sz_per_dev + 1);
+ cali_ctrls[i].name = cali_name;
+ cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrls[i].info = snd_soc_bytes_info_ext;
+ cali_ctrls[i].get = tasdev_cali_data_get;
+ cali_ctrls[i].put = tasdev_cali_data_put;
+ cali_ctrls[i].private_value = (unsigned long)ext_cali_data;
+ i++;
+
+ cali_data->data = devm_kzalloc(priv->dev, cali_data->total_sz,
+ GFP_KERNEL);
+ if (!cali_data->data)
+ return -ENOMEM;
+
+ if (priv->chip_id == TAS2781) {
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Calibration Start", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2781 ftc start:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for pilot threshold, pilot tone
+ * and sine gain (12 bytes)
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (8 bytes)
+ * }
+ */
+ ext_cali_start->max = 14 + priv->ndev * 9;
+ cali_ctrls[i].name = cali_start_name;
+ cali_ctrls[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrls[i].info = snd_soc_bytes_info_ext;
+ cali_ctrls[i].put = tas2781_calib_start_put;
+ cali_ctrls[i].get = tasdev_nop_get;
+ cali_ctrls[i].private_value = (unsigned long)ext_cali_start;
+ i++;
+ }
+
+ return snd_soc_add_component_controls(priv->codec, cali_ctrls,
+ nctrls < i ? nctrls : i);
+}
+
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+/*
+ * This debugfs node is a bridge to the acoustic tuning application
+ * tool which can tune the chips' acoustic effect.
+ *
+ * package structure for PPC3 communications:
+ * Pkg len (1 byte)
+ * Pkg id (1 byte, 'r' or 'w')
+ * Dev id (1 byte, i2c address)
+ * Book id (1 byte)
+ * Page id (1 byte)
+ * Reg id (1 byte)
+ * switch (pkg id) {
+ * case 'w':
+ * 1 byte, length of data to read
+ * case 'r':
+ * data payload (1~128 bytes)
+ * }
+ */
+static ssize_t acoustic_ctl_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &tas_priv->acou_data;
+ int ret = -1;
+
+ if (p->id == 'r' && p->len == count && count <= sizeof(*p))
+ ret = simple_read_from_buffer(to, count, ppos, p, p->len);
+ else
+ dev_err(tas_priv->dev, "Not ready for get.\n");
+ return ret;
+}
+
+static ssize_t acoustic_ctl_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &priv->acou_data;
+ unsigned int max_pkg_len = sizeof(*p);
+ unsigned char *src;
+ int j, len, reg, val;
+ unsigned short chn;
+ int ret = -1;
+
+ if (count > sizeof(*p)) {
+ dev_err(priv->dev, "count(%u) is larger than max(%u).\n",
+ (unsigned int)count, max_pkg_len);
+ return ret;
+ }
+
+ src = memdup_user(from, count);
+ if (IS_ERR(src))
+ return PTR_ERR(src);
+
+ if (src[0] > max_pkg_len && src[0] != count) {
+ dev_err(priv->dev, "pkg(%u), max(%u), count(%u) mismatch.\n",
+ src[0], max_pkg_len, (unsigned int)count);
+ ret = 0;
+ goto exit;
+ }
+
+ switch (src[1]) {
+ case 'r':
+ /* length of data to read */
+ len = src[6];
+ break;
+ case 'w':
+ /* Skip 6 bytes for package type and register address */
+ len = src[0] - 6;
+ break;
+ default:
+ dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]);
+ ret = 0;
+ goto exit;
+ }
+
+ if (len < 1) {
+ dev_err(priv->dev, "pkg fmt invalid %02x.\n", len);
+ ret = 0;
+ goto exit;
+ }
+
+ for (j = 0; j < priv->ndev; j++)
+ if (src[2] == priv->tasdevice[j].dev_addr) {
+ chn = j;
+ break;
+ }
+ if (j >= priv->ndev) {
+ dev_err(priv->dev, "no such device 0x%02x.\n", src[2]);
+ ret = 0;
+ goto exit;
+ }
+
+ reg = TASDEVICE_REG(src[3], src[4], src[5]);
+
+ guard(mutex)(&priv->codec_lock);
+
+ if (src[1] == 'w') {
+ if (len > 1)
+ ret = tasdevice_dev_bulk_write(priv, chn, reg,
+ &src[6], len);
+ else
+ ret = tasdevice_dev_write(priv, chn, reg, src[6]);
+ } else {
+ struct acoustic_data *p = &priv->acou_data;
+
+ memcpy(p, src, 6);
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_read(priv, chn, reg,
+ p->data, len);
+ } else {
+ ret = tasdevice_dev_read(priv, chn, reg, &val);
+ p->data[0] = val;
+ }
+ p->len = len + 6;
+ }
+
+ if (ret)
+ dev_err(priv->dev, "i2c communication error.\n");
+ else
+ ret = count;
+exit:
+ kfree(src);
+ return ret;
+}
+
+static const struct file_operations acoustic_ctl_fops = {
+ .open = simple_open,
+ .read = acoustic_ctl_read,
+ .write = acoustic_ctl_write,
+};
+#endif
+
+static void tasdevice_fw_ready(const struct firmware *fmw,
+ void *context)
+{
+ struct tasdevice_priv *tas_priv = context;
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ struct snd_soc_component *comp = tas_priv->codec;
+ struct dentry *debugfs_root = comp->debugfs_root;
+ char *acoustic_debugfs_node;
+#endif
+ int ret = 0;
+ int i;
+
+ mutex_lock(&tas_priv->codec_lock);
+
+ ret = tasdevice_rca_parser(tas_priv, fmw);
+ if (ret) {
+ tasdevice_config_info_remove(tas_priv);
+ goto out;
+ }
+ tasdevice_create_control(tas_priv);
+
+ tasdevice_dsp_remove(tas_priv);
+ tasdevice_calbin_remove(tas_priv);
+ /*
+ * The baseline is the RCA-only case, and then the code attempts to
+ * load DSP firmware but in case of failures just keep going, i.e.
+ * failing to load DSP firmware is NOT an error.
+ */
+ tas_priv->fw_state = TASDEVICE_RCA_FW_OK;
+ /* There is no DSP firmware required for TAS2118/2X20/257X. */
+ switch (tas_priv->chip_id) {
+ case TAS2020:
+ case TAS2118:
+ case TAS2120:
+ case TAS2320:
+ case TAS2570:
+ case TAS2572:
+ goto out;
+ }
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->coef_binaryname, 64, "%s-%s_coef.bin",
+ tas_priv->name_prefix, tas_priv->dev_name);
+ else
+ 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);
+ goto out;
+ }
+
+ /*
+ * If no dsp-related kcontrol created, the dsp resource will be freed.
+ */
+ ret = tasdevice_dsp_create_ctrls(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "dsp controls error\n");
+ goto out;
+ }
+ tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
+
+ /* There is no calibration required for TAS5825/TAS5827. */
+ if (tas_priv->chip_id < TAS5825) {
+ ret = tasdevice_create_cali_ctrls(tas_priv);
+ if (ret) {
+ dev_err(tas_priv->dev, "cali controls error\n");
+ goto out;
+ }
+ /* If calibrated data occurs error, dsp will still works
+ * with default calibrated data inside algo.
+ */
+ for (i = 0; i < tas_priv->ndev; i++) {
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->cal_binaryname[i], 64,
+ "%s-%s_cal_0x%02x.bin",
+ tas_priv->name_prefix,
+ tas_priv->dev_name,
+ tas_priv->tasdevice[i].dev_addr);
+ else
+ 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, keep default.\n",
+ __func__, tas_priv->cal_binaryname[i]);
+ }
+ }
+
+ tasdevice_prmg_load(tas_priv, 0);
+ tas_priv->cur_prog = 0;
+
+ /* Init common setting for different audio profiles */
+ if (tas_priv->rcabin.init_profile_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv,
+ tas_priv->rcabin.init_profile_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ if (tas_priv->name_prefix)
+ acoustic_debugfs_node = devm_kasprintf(tas_priv->dev,
+ GFP_KERNEL, "%s_acoustic_ctl", tas_priv->name_prefix);
+ else
+ acoustic_debugfs_node = devm_kstrdup(tas_priv->dev,
+ "acoustic_ctl", GFP_KERNEL);
+ debugfs_create_file(acoustic_debugfs_node, 0644, debugfs_root,
+ comp, &acoustic_ctl_fops);
+#endif
+out:
+ if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) {
+ switch (tas_priv->chip_id) {
+ case TAS2563:
+ case TAS2781:
+ case TAS5825:
+ case TAS5827:
+ /* If DSP FW fail, DSP kcontrol won't be created. */
+ tasdevice_dsp_remove(tas_priv);
+ }
+ }
+ mutex_unlock(&tas_priv->codec_lock);
+ 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);
+
+ switch (tas_priv->fw_state) {
+ case TASDEVICE_RCA_FW_OK:
+ case TASDEVICE_DSP_FW_ALL_OK:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+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;
+
+ 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);
+ return -EINVAL;
+ }
+
+ 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);
+ return -EINVAL;
+ }
+
+ 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);
+ return bclk_rate;
+ }
+
+ return 0;
+}
+
+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 = "tasdev_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);
+ struct snd_kcontrol_new *p;
+ unsigned int size;
+ int rc;
+
+ switch (tas_priv->chip_id) {
+ case TAS2020:
+ case TAS2118:
+ case TAS2120:
+ case TAS2320:
+ case TAS2570:
+ case TAS2572:
+ p = (struct snd_kcontrol_new *)tas2x20_snd_controls;
+ size = ARRAY_SIZE(tas2x20_snd_controls);
+ tas_priv->dvc_tlv_table = tas2x20_dvc_table;
+ break;
+ case TAS2781:
+ p = (struct snd_kcontrol_new *)tas2781_snd_controls;
+ size = ARRAY_SIZE(tas2781_snd_controls);
+ break;
+ case TAS5825:
+ case TAS5827:
+ p = (struct snd_kcontrol_new *)tas5825_snd_controls;
+ size = ARRAY_SIZE(tas5825_snd_controls);
+ break;
+ default:
+ p = (struct snd_kcontrol_new *)tas2563_snd_controls;
+ size = ARRAY_SIZE(tas2563_snd_controls);
+ tas_priv->dvc_tlv_table = tas2563_dvc_table;
+ break;
+ }
+
+ rc = snd_soc_add_component_controls(codec, p, size);
+ if (rc < 0) {
+ dev_err(tas_priv->dev, "%s: Add control err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
+ tas_priv->name_prefix = codec->name_prefix;
+ return tascodec_init(tas_priv, codec, THIS_MODULE, tasdevice_fw_ready);
+}
+
+static void tasdevice_deinit(void *context)
+{
+ struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context;
+ struct tasdevice *tasdev = tas_priv->tasdevice;
+ int i;
+
+ for (i = 0; i < tas_priv->ndev; i++)
+ kfree(tasdev[i].cali_data_backup);
+
+ 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,
+ .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 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 =
+ acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0);
+ } else if (IS_ENABLED(CONFIG_OF)) {
+ struct device_node *np = tas_priv->dev->of_node;
+ u64 addr;
+
+ for (i = 0; i < TASDEVICE_MAX_CHANNELS; i++) {
+ if (of_property_read_reg(np, i, &addr, NULL))
+ break;
+ dev_addrs[ndev++] = addr;
+ }
+
+ tas_priv->irq = of_irq_get(np, 0);
+ } else {
+ ndev = 1;
+ dev_addrs[0] = client->addr;
+ }
+ 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", 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);
+}
+
+static int tasdevice_i2c_probe(struct i2c_client *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 = (uintptr_t)i2c_get_match_data(i2c);
+ tas_priv->isacpi = false;
+ }
+
+ tasdevice_parse_dt(tas_priv);
+
+ ret = tasdevice_init(tas_priv);
+ if (ret)
+ goto err;
+
+ tasdevice_reset(tas_priv);
+
+ 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[] = {
+ { "TXNW2020", TAS2020 },
+ { "TXNW2118", TAS2118 },
+ { "TXNW2120", TAS2120 },
+ { "TXNW2320", TAS2320 },
+ { "TXNW2563", TAS2563 },
+ { "TXNW2570", TAS2570 },
+ { "TXNW2572", TAS2572 },
+ { "TXNW2781", TAS2781 },
+ { "TXNW5825", TAS5825 },
+ { "TXNW5827", TAS5827 },
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match);
+#endif
+
+static struct i2c_driver tasdevice_i2c_driver = {
+ .driver = {
+ .name = "tasdev-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 <shenghao-ding@ti.com>");
+MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>");
+MODULE_DESCRIPTION("ASoC TAS2781 Driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_TAS2781_FMWLIB");
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
new file mode 100644
index 000000000000..1fb4227b711e
--- /dev/null
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+//
+// Copyright (C) 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// The TAS2783 driver implements a flexible and configurable
+// algo coefficient setting for single TAS2783 chips.
+//
+// Author: Niranjan H Y <niranjanhy@ti.com>
+// Author: Baojun Xu <baojun.xu@ti.com>
+// Author: Kevin Lu <kevin-lu@ti.com>
+
+#include <linux/unaligned.h>
+#include <linux/crc32.h>
+#include <linux/efi.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/wait.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/tas2781-tlv.h>
+
+#include "tas2783.h"
+
+#define TIMEOUT_FW_DL_MS (3000)
+#define FW_DL_OFFSET 36
+#define FW_FL_HDR 12
+#define TAS2783_PROBE_TIMEOUT 5000
+#define TAS2783_CALI_GUID EFI_GUID(0x1f52d2a1, 0xbb3a, 0x457d, 0xbc, \
+ 0x09, 0x43, 0xa3, 0xf4, 0x31, 0x0a, 0x92)
+
+static const u32 tas2783_cali_reg[] = {
+ TAS2783_CAL_R0,
+ TAS2783_CAL_INVR0,
+ TAS2783_CAL_R0LOW,
+ TAS2783_CAL_POWER,
+ TAS2783_CAL_TLIM,
+};
+
+struct bin_header_t {
+ u16 vendor_id;
+ u16 version;
+ u32 file_id;
+ u32 length;
+};
+
+struct calibration_data {
+ u32 is_valid;
+ unsigned long read_sz;
+ u8 data[TAS2783_CALIB_DATA_SZ];
+};
+
+struct tas2783_prv {
+ struct snd_soc_component *component;
+ struct calibration_data cali_data;
+ struct sdw_slave *sdw_peripheral;
+ enum sdw_slave_status status;
+ /* calibration */
+ struct mutex calib_lock;
+ /* pde and firmware download */
+ struct mutex pde_lock;
+ struct regmap *regmap;
+ struct device *dev;
+ struct class *class;
+ struct attribute_group *cal_attr_groups;
+ struct tm tm;
+ u8 rca_binaryname[64];
+ u8 dev_name[32];
+ bool hw_init;
+ /* wq for firmware download */
+ wait_queue_head_t fw_wait;
+ bool fw_dl_task_done;
+ bool fw_dl_success;
+};
+
+static const struct reg_default tas2783_reg_default[] = {
+ {TAS2783_AMP_LEVEL, 0x28},
+ {TASDEV_REG_SDW(0, 0, 0x03), 0x28},
+ {TASDEV_REG_SDW(0, 0, 0x04), 0x21},
+ {TASDEV_REG_SDW(0, 0, 0x05), 0x41},
+ {TASDEV_REG_SDW(0, 0, 0x06), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x07), 0x20},
+ {TASDEV_REG_SDW(0, 0, 0x08), 0x09},
+ {TASDEV_REG_SDW(0, 0, 0x09), 0x02},
+ {TASDEV_REG_SDW(0, 0, 0x0a), 0x0a},
+ {TASDEV_REG_SDW(0, 0, 0x0c), 0x10},
+ {TASDEV_REG_SDW(0, 0, 0x0d), 0x13},
+ {TASDEV_REG_SDW(0, 0, 0x0e), 0xc2},
+ {TASDEV_REG_SDW(0, 0, 0x0f), 0x40},
+ {TASDEV_REG_SDW(0, 0, 0x10), 0x04},
+ {TASDEV_REG_SDW(0, 0, 0x13), 0x13},
+ {TASDEV_REG_SDW(0, 0, 0x14), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x15), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x16), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x17), 0x80},
+ {TAS2783_DVC_LVL, 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x1b), 0x61},
+ {TASDEV_REG_SDW(0, 0, 0x1c), 0x36},
+ {TASDEV_REG_SDW(0, 0, 0x1d), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x1f), 0x01},
+ {TASDEV_REG_SDW(0, 0, 0x20), 0x2e},
+ {TASDEV_REG_SDW(0, 0, 0x21), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x34), 0x06},
+ {TASDEV_REG_SDW(0, 0, 0x35), 0xbd},
+ {TASDEV_REG_SDW(0, 0, 0x36), 0xad},
+ {TASDEV_REG_SDW(0, 0, 0x37), 0xa8},
+ {TASDEV_REG_SDW(0, 0, 0x38), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x3b), 0xfc},
+ {TASDEV_REG_SDW(0, 0, 0x3d), 0xdd},
+ {TASDEV_REG_SDW(0, 0, 0x40), 0xf6},
+ {TASDEV_REG_SDW(0, 0, 0x41), 0x14},
+ {TASDEV_REG_SDW(0, 0, 0x5c), 0x19},
+ {TASDEV_REG_SDW(0, 0, 0x5d), 0x80},
+ {TASDEV_REG_SDW(0, 0, 0x63), 0x48},
+ {TASDEV_REG_SDW(0, 0, 0x65), 0x08},
+ {TASDEV_REG_SDW(0, 0, 0x66), 0xb2},
+ {TASDEV_REG_SDW(0, 0, 0x67), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6a), 0x12},
+ {TASDEV_REG_SDW(0, 0, 0x6b), 0xfb},
+ {TASDEV_REG_SDW(0, 0, 0x6c), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6d), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x6e), 0x1a},
+ {TASDEV_REG_SDW(0, 0, 0x6f), 0x00},
+ {TASDEV_REG_SDW(0, 0, 0x70), 0x96},
+ {TASDEV_REG_SDW(0, 0, 0x71), 0x02},
+ {TASDEV_REG_SDW(0, 0, 0x73), 0x08},
+ {TASDEV_REG_SDW(0, 0, 0x75), 0xe0},
+ {TASDEV_REG_SDW(0, 0, 0x7a), 0x60},
+ {TASDEV_REG_SDW(0, 0, 0x60), 0x21},
+ {TASDEV_REG_SDW(0, 1, 0x02), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x17), 0xc0},
+ {TASDEV_REG_SDW(0, 1, 0x19), 0x60},
+ {TASDEV_REG_SDW(0, 1, 0x35), 0x75},
+ {TASDEV_REG_SDW(0, 1, 0x3d), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x3e), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x3f), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x40), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x41), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x42), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x43), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x44), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x45), 0x00},
+ {TASDEV_REG_SDW(0, 1, 0x47), 0xab},
+ {TASDEV_REG_SDW(0, 0xfd, 0x0d), 0x0d},
+ {TASDEV_REG_SDW(0, 0xfd, 0x39), 0x00},
+ {TASDEV_REG_SDW(0, 0xfd, 0x3e), 0x00},
+ {TASDEV_REG_SDW(0, 0xfd, 0x45), 0x00},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1), 0x9c00},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 3), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 4), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 5), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 2), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 3), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 4), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xb), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xc), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0},
+ {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0},
+};
+
+static const struct reg_sequence tas2783_init_seq[] = {
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0x00), 0x04),
+ REG_SEQ0(0x00800418, 0x00),
+ REG_SEQ0(0x00800419, 0x00),
+ REG_SEQ0(0x0080041a, 0x00),
+ REG_SEQ0(0x0080041b, 0x00),
+ REG_SEQ0(0x00800428, 0x40),
+ REG_SEQ0(0x00800429, 0x00),
+ REG_SEQ0(0x0080042a, 0x00),
+ REG_SEQ0(0x0080042b, 0x00),
+ REG_SEQ0(SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x1, 0x00), 0x00),
+ REG_SEQ0(0x0080005c, 0xD9),
+ REG_SEQ0(0x00800082, 0x20),
+ REG_SEQ0(0x008000a1, 0x00),
+ REG_SEQ0(0x00800097, 0xc8),
+ REG_SEQ0(0x00800099, 0x20),
+ REG_SEQ0(0x008000c7, 0xaa),
+ REG_SEQ0(0x008000b5, 0x74),
+ REG_SEQ0(0x00800082, 0x20),
+ REG_SEQ0(0x00807e8d, 0x0d),
+ REG_SEQ0(0x00807eb9, 0x53),
+ REG_SEQ0(0x00807ebe, 0x42),
+ REG_SEQ0(0x00807ec5, 0x37),
+ REG_SEQ0(0x00800066, 0x92),
+ REG_SEQ0(0x00800003, 0x28),
+ REG_SEQ0(0x00800004, 0x21),
+ REG_SEQ0(0x00800005, 0x41),
+ REG_SEQ0(0x00800006, 0x00),
+ REG_SEQ0(0x00800007, 0x20),
+ REG_SEQ0(0x0080000c, 0x10),
+ REG_SEQ0(0x00800013, 0x08),
+ REG_SEQ0(0x00800015, 0x00),
+ REG_SEQ0(0x00800017, 0x80),
+ REG_SEQ0(0x0080001a, 0x00),
+ REG_SEQ0(0x0080001b, 0x22),
+ REG_SEQ0(0x0080001c, 0x36),
+ REG_SEQ0(0x0080001d, 0x01),
+ REG_SEQ0(0x0080001f, 0x00),
+ REG_SEQ0(0x00800020, 0x2e),
+ REG_SEQ0(0x00800034, 0x06),
+ REG_SEQ0(0x00800035, 0xb9),
+ REG_SEQ0(0x00800036, 0xad),
+ REG_SEQ0(0x00800037, 0xa8),
+ REG_SEQ0(0x00800038, 0x00),
+ REG_SEQ0(0x0080003b, 0xfc),
+ REG_SEQ0(0x0080003d, 0xdd),
+ REG_SEQ0(0x00800040, 0xf6),
+ REG_SEQ0(0x00800041, 0x14),
+ REG_SEQ0(0x0080005c, 0x19),
+ REG_SEQ0(0x0080005d, 0x80),
+ REG_SEQ0(0x00800063, 0x48),
+ REG_SEQ0(0x00800065, 0x08),
+ REG_SEQ0(0x00800067, 0x00),
+ REG_SEQ0(0x0080006a, 0x12),
+ REG_SEQ0(0x0080006b, 0x7b),
+ REG_SEQ0(0x0080006c, 0x00),
+ REG_SEQ0(0x0080006d, 0x00),
+ REG_SEQ0(0x0080006e, 0x1a),
+ REG_SEQ0(0x0080006f, 0x00),
+ REG_SEQ0(0x00800070, 0x96),
+ REG_SEQ0(0x00800071, 0x02),
+ REG_SEQ0(0x00800073, 0x08),
+ REG_SEQ0(0x00800075, 0xe0),
+ REG_SEQ0(0x0080007a, 0x60),
+ REG_SEQ0(0x008000bd, 0x00),
+ REG_SEQ0(0x008000be, 0x00),
+ REG_SEQ0(0x008000bf, 0x00),
+ REG_SEQ0(0x008000c0, 0x00),
+ REG_SEQ0(0x008000c1, 0x00),
+ REG_SEQ0(0x008000c2, 0x00),
+ REG_SEQ0(0x008000c3, 0x00),
+ REG_SEQ0(0x008000c4, 0x00),
+ REG_SEQ0(0x008000c5, 0x00),
+ REG_SEQ0(0x00800008, 0x49),
+ REG_SEQ0(0x00800009, 0x02),
+ REG_SEQ0(0x0080000a, 0x1a),
+ REG_SEQ0(0x0080000d, 0x93),
+ REG_SEQ0(0x0080000e, 0x82),
+ REG_SEQ0(0x0080000f, 0x42),
+ REG_SEQ0(0x00800010, 0x84),
+ REG_SEQ0(0x00800014, 0x0a),
+ REG_SEQ0(0x00800016, 0x00),
+ REG_SEQ0(0x00800060, 0x21),
+};
+
+static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
+{
+ switch (reg) {
+ case 0x000 ... 0x080: /* Data port 0. */
+ case 0x100 ... 0x140: /* Data port 1. */
+ case 0x200 ... 0x240: /* Data port 2. */
+ case 0x300 ... 0x340: /* Data port 3. */
+ case 0x400 ... 0x440: /* Data port 4. */
+ case 0x500 ... 0x540: /* Data port 5. */
+ case 0x800000 ... 0x803fff: /* Page 0 ~ 127. */
+ case 0x807e80 ... 0x807eff: /* Page 253. */
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23,
+ TAS2783_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, TAS2783_SDCA_CTL_FU_MUTE,
+ TAS2783_DEVICE_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 3):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 4):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 8):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 9):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xa):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xb):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xc):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xd):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xe):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 0xf):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS25, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS25, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0):
+ return 1;
+
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x11, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 3):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 4):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 5):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, 0x02, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0):
+ return 2;
+
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0):
+ case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0):
+ return 4;
+
+ default:
+ return 0;
+ }
+}
+
+static bool tas2783_readable_register(struct device *dev, unsigned int reg)
+{
+ return tas2783_sdca_mbq_size(dev, reg) > 0;
+}
+
+static bool tas2783_volatile_register(struct device *dev, u32 reg)
+{
+ switch (reg) {
+ case 0x000 ... 0x080: /* Data port 0. */
+ case 0x100 ... 0x140: /* Data port 1. */
+ case 0x200 ... 0x240: /* Data port 2. */
+ case 0x300 ... 0x340: /* Data port 3. */
+ case 0x400 ... 0x440: /* Data port 4. */
+ case 0x500 ... 0x540: /* Data port 5. */
+ case 0x800001:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config tas_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = tas2783_readable_register,
+ .volatile_reg = tas2783_volatile_register,
+ .reg_defaults = tas2783_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(tas2783_reg_default),
+ .max_register = 0x41008000 + TASDEV_REG_SDW(0xa1, 0x60, 0x7f),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_sdw_mbq_cfg tas2783_mbq_cfg = {
+ .mbq_size = tas2783_sdca_mbq_size,
+};
+
+static s32 tas2783_digital_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_digital_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_amp_getvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static s32 tas2783_amp_putvol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static const struct snd_kcontrol_new tas2783_snd_controls[] = {
+ SOC_SINGLE_RANGE_EXT_TLV("Amp Volume", TAS2783_AMP_LEVEL,
+ 1, 0, 20, 0, tas2783_amp_getvol,
+ tas2783_amp_putvol, tas2781_amp_tlv),
+ SOC_SINGLE_RANGE_EXT_TLV("Speaker Volume", TAS2783_DVC_LVL,
+ 0, 0, 200, 1, tas2783_digital_getvol,
+ tas2783_digital_putvol, tas2781_dvc_tlv),
+};
+
+static s32 tas2783_validate_calibdata(struct tas2783_prv *tas_dev,
+ u8 *data, u32 size)
+{
+ u32 ts, spk_count, size_calculated;
+ u32 crc_calculated, crc_read, i;
+ u32 *tmp_val;
+ struct tm tm;
+
+ i = 0;
+ tmp_val = (u32 *)data;
+ if (tmp_val[i++] != 2783) {
+ dev_err(tas_dev->dev, "cal data magic number mismatch");
+ return -EINVAL;
+ }
+
+ spk_count = tmp_val[i++];
+ if (spk_count > TAS2783_CALIB_MAX_SPK_COUNT) {
+ dev_err(tas_dev->dev, "cal data spk_count too large");
+ return -EINVAL;
+ }
+
+ ts = tmp_val[i++];
+ time64_to_tm(ts, 0, &tm);
+ dev_dbg(tas_dev->dev, "cal data timestamp: %ld-%d-%d %d:%d:%d",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ size_calculated =
+ (spk_count * TAS2783_CALIB_PARAMS * sizeof(u32)) +
+ TAS2783_CALIB_HDR_SZ + TAS2783_CALIB_CRC_SZ;
+ if (size_calculated > TAS2783_CALIB_DATA_SZ) {
+ dev_err(tas_dev->dev, "cali data sz too large");
+ return -EINVAL;
+ } else if (size < size_calculated) {
+ dev_err(tas_dev->dev, "cali data size mismatch calc=%u vs %d\n",
+ size, size_calculated);
+ return -EINVAL;
+ }
+
+ crc_calculated = crc32(~0, data,
+ size_calculated - TAS2783_CALIB_CRC_SZ) ^ ~0;
+ crc_read = tmp_val[(size_calculated - TAS2783_CALIB_CRC_SZ) / sizeof(u32)];
+ if (crc_calculated != crc_read) {
+ dev_err(tas_dev->dev,
+ "calib data integrity check fail, 0x%08x vs 0x%08x\n",
+ crc_calculated, crc_read);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32 *cali_data)
+{
+ u32 dev_count, offset, i, device_num;
+ u32 reg_value;
+ u8 buf[4];
+
+ dev_count = cali_data[1];
+ offset = 3;
+
+ for (device_num = 0; device_num < dev_count; device_num++) {
+ if (cali_data[offset] != tas_dev->sdw_peripheral->id.unique_id) {
+ offset += TAS2783_CALIB_PARAMS;
+ continue;
+ }
+ offset++;
+
+ for (i = 0; i < ARRAY_SIZE(tas2783_cali_reg); i++) {
+ reg_value = cali_data[offset + i];
+ buf[0] = reg_value >> 24;
+ buf[1] = reg_value >> 16;
+ buf[2] = reg_value >> 8;
+ buf[3] = reg_value & 0xff;
+ regmap_bulk_write(tas_dev->regmap, tas2783_cali_reg[i],
+ buf, sizeof(u32));
+ }
+ break;
+ }
+
+ if (device_num == dev_count)
+ dev_err(tas_dev->dev, "device not found\n");
+ else
+ dev_dbg(tas_dev->dev, "calib data update done\n");
+}
+
+static s32 tas2783_update_calibdata(struct tas2783_prv *tas_dev)
+{
+ efi_guid_t efi_guid = TAS2783_CALI_GUID;
+ u32 attr, i, *tmp_val;
+ unsigned long size;
+ s32 ret;
+ efi_status_t status;
+ static efi_char16_t efi_names[][32] = {
+ L"SmartAmpCalibrationData", L"CALI_DATA"};
+
+ tmp_val = (u32 *)tas_dev->cali_data.data;
+ attr = 0;
+ i = 0;
+
+ /*
+ * In some cases, the calibration is performed in Windows,
+ * and data was saved in UEFI. Linux can access it.
+ */
+ for (i = 0; i < ARRAY_SIZE(efi_names); i++) {
+ size = 0;
+ status = efi.get_variable(efi_names[i], &efi_guid, &attr,
+ &size, NULL);
+ if (size > TAS2783_CALIB_DATA_SZ) {
+ dev_err(tas_dev->dev, "cali data too large\n");
+ break;
+ }
+
+ tas_dev->cali_data.read_sz = size;
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ status = efi.get_variable(efi_names[i], &efi_guid, &attr,
+ &tas_dev->cali_data.read_sz,
+ tas_dev->cali_data.data);
+ dev_dbg(tas_dev->dev, "cali get %lu bytes result:%ld\n",
+ tas_dev->cali_data.read_sz, status);
+ }
+ if (status == EFI_SUCCESS)
+ break;
+ }
+
+ if (status != EFI_SUCCESS) {
+ /* Failed got calibration data from EFI. */
+ dev_dbg(tas_dev->dev, "No calibration data in UEFI.");
+ return 0;
+ }
+
+ mutex_lock(&tas_dev->calib_lock);
+ ret = tas2783_validate_calibdata(tas_dev, tas_dev->cali_data.data,
+ tas_dev->cali_data.read_sz);
+ if (!ret)
+ tas2783_set_calib_params_to_device(tas_dev, tmp_val);
+ mutex_unlock(&tas_dev->calib_lock);
+
+ return ret;
+}
+
+static s32 read_header(const u8 *data, struct bin_header_t *hdr)
+{
+ hdr->vendor_id = get_unaligned_le16(&data[0]);
+ hdr->file_id = get_unaligned_le32(&data[2]);
+ hdr->version = get_unaligned_le16(&data[6]);
+ hdr->length = get_unaligned_le32(&data[8]);
+ return 12;
+}
+
+static void tas2783_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tas2783_prv *tas_dev =
+ (struct tas2783_prv *)context;
+ const u8 *buf = NULL;
+ s32 offset = 0, img_sz, file_blk_size, ret;
+ struct bin_header_t hdr;
+
+ if (!fmw || !fmw->data) {
+ /* No firmware binary, devices will work in ROM mode. */
+ dev_err(tas_dev->dev,
+ "Failed to read %s, no side-effect on driver running\n",
+ tas_dev->rca_binaryname);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_lock(&tas_dev->pde_lock);
+ img_sz = fmw->size;
+ buf = fmw->data;
+ offset += FW_DL_OFFSET;
+ while (offset < (img_sz - FW_FL_HDR)) {
+ memset(&hdr, 0, sizeof(hdr));
+ offset += read_header(&buf[offset], &hdr);
+ dev_dbg(tas_dev->dev,
+ "vndr=%d, file=%d, version=%d, len=%d, off=%d\n",
+ hdr.vendor_id, hdr.file_id, hdr.version,
+ hdr.length, offset);
+ /* size also includes the header */
+ file_blk_size = hdr.length - FW_FL_HDR;
+
+ switch (hdr.file_id) {
+ case 0:
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ PRAM_ADDR_START, file_blk_size,
+ &buf[offset]);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "PRAM update failed: %d", ret);
+ break;
+
+ case 1:
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ YRAM_ADDR_START, file_blk_size,
+ &buf[offset]);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "YRAM update failed: %d", ret);
+
+ break;
+
+ default:
+ ret = -EINVAL;
+ dev_err(tas_dev->dev, "Unsupported file");
+ break;
+ }
+
+ if (ret == 0)
+ offset += file_blk_size;
+ else
+ break;
+ }
+ mutex_unlock(&tas_dev->pde_lock);
+ tas2783_update_calibdata(tas_dev);
+
+out:
+ if (!ret)
+ tas_dev->fw_dl_success = true;
+ tas_dev->fw_dl_task_done = true;
+ wake_up(&tas_dev->fw_wait);
+ if (fmw)
+ release_firmware(fmw);
+}
+
+static inline s32 tas_clear_latch(struct tas2783_prv *priv)
+{
+ return regmap_update_bits(priv->regmap,
+ TASDEV_REG_SDW(0, 0, 0x5c),
+ 0x04, 0x04);
+}
+
+static s32 tas_fu21_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, s32 event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component);
+ s32 mute;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ mute = 1;
+ break;
+ }
+
+ return sdw_write_no_pm(tas_dev->sdw_peripheral,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21,
+ TAS2783_SDCA_CTL_FU_MUTE, 1), mute);
+}
+
+static s32 tas_fu23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, s32 event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(component);
+ s32 mute;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ mute = 0;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ mute = 1;
+ break;
+ }
+
+ return sdw_write_no_pm(tas_dev->sdw_peripheral,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23,
+ TAS2783_SDCA_CTL_FU_MUTE, 1), mute);
+}
+
+static const struct snd_soc_dapm_widget tas_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_DAC_E("FU21", NULL, SND_SOC_NOPM, 0, 0, tas_fu21_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU23", NULL, SND_SOC_NOPM, 0, 0, tas_fu23_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route tas_audio_map[] = {
+ {"FU21", NULL, "ASI"},
+ {"SPK", NULL, "FU21"},
+ {"FU23", NULL, "ASI"},
+ {"SPK", NULL, "FU23"},
+ {"ASI OUT", NULL, "DMIC"},
+};
+
+static s32 tas_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, s32 direction)
+{
+ if (!sdw_stream)
+ return 0;
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void tas_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static s32 tas_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 tas2783_prv *tas_dev =
+ 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;
+ struct sdw_slave *sdw_peripheral = tas_dev->sdw_peripheral;
+ s32 ret, retry = 3;
+
+ if (!tas_dev->fw_dl_success) {
+ dev_err(tas_dev->dev, "error playback without fw download");
+ return -EINVAL;
+ }
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream)
+ return -EINVAL;
+
+ ret = tas_clear_latch(tas_dev);
+ if (ret)
+ dev_err(tas_dev->dev,
+ "clear latch failed, err=%d", ret);
+
+ mutex_lock(&tas_dev->pde_lock);
+ /*
+ * Sometimes, there is error returned during power on.
+ * So added retry logic to ensure power on so that
+ * port prepare succeeds
+ */
+ do {
+ ret = regmap_write(tas_dev->regmap,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
+ TAS2783_SDCA_POW_STATE_ON);
+ if (!ret)
+ break;
+ usleep_range(2000, 2200);
+ } while (retry--);
+ mutex_unlock(&tas_dev->pde_lock);
+ if (ret)
+ return ret;
+
+ /* 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;
+
+ ret = sdw_stream_add_slave(sdw_peripheral,
+ &stream_config, &port_config, 1, sdw_stream);
+ if (ret)
+ dev_err(dai->dev, "Unable to configure port\n");
+
+ return ret;
+}
+
+static s32 tas_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ s32 ret;
+ struct snd_soc_component *component = dai->component;
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ sdw_stream_remove_slave(tas_dev->sdw_peripheral, sdw_stream);
+
+ mutex_lock(&tas_dev->pde_lock);
+ ret = regmap_write(tas_dev->regmap,
+ SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23,
+ TAS2783_SDCA_CTL_REQ_POW_STATE, 0),
+ TAS2783_SDCA_POW_STATE_OFF);
+ mutex_unlock(&tas_dev->pde_lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tas_dai_ops = {
+ .hw_params = tas_sdw_hw_params,
+ .hw_free = tas_sdw_pcm_hw_free,
+ .set_stream = tas_set_sdw_stream,
+ .shutdown = tas_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver tas_dai_driver[] = {
+ {
+ .name = "tas2783-codec",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAS2783_DEVICE_RATES,
+ .formats = TAS2783_DEVICE_FORMATS,
+ },
+ .ops = &tas_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static s32 tas_component_probe(struct snd_soc_component *component)
+{
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(component);
+
+ tas_dev->component = component;
+ tas25xx_register_misc(tas_dev->sdw_peripheral);
+
+ return 0;
+}
+
+static void tas_component_remove(struct snd_soc_component *codec)
+{
+ struct tas2783_prv *tas_dev =
+ snd_soc_component_get_drvdata(codec);
+ tas25xx_deregister_misc();
+ tas_dev->component = NULL;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_tasdevice = {
+ .probe = tas_component_probe,
+ .remove = tas_component_remove,
+ .controls = tas2783_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2783_snd_controls),
+ .dapm_widgets = tas_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas_dapm_widgets),
+ .dapm_routes = tas_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static s32 tas_init(struct tas2783_prv *tas_dev)
+{
+ s32 ret;
+
+ dev_set_drvdata(tas_dev->dev, tas_dev);
+ ret = devm_snd_soc_register_component(tas_dev->dev,
+ &soc_codec_driver_tasdevice,
+ tas_dai_driver,
+ ARRAY_SIZE(tas_dai_driver));
+ if (ret) {
+ dev_err(tas_dev->dev, "%s: codec register error:%d.\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(tas_dev->dev, 3000);
+ pm_runtime_use_autosuspend(tas_dev->dev);
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(tas_dev->dev);
+ pm_runtime_enable(tas_dev->dev);
+
+ return ret;
+}
+
+static s32 tas_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ s32 nval;
+ s32 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 = false;
+ 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 = false;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ return 0;
+}
+
+static s32 tas2783_sdca_dev_suspend(struct device *dev)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+
+ if (!tas_dev->hw_init)
+ return 0;
+
+ regcache_cache_only(tas_dev->regmap, true);
+ return 0;
+}
+
+static s32 tas2783_sdca_dev_system_suspend(struct device *dev)
+{
+ return tas2783_sdca_dev_suspend(dev);
+}
+
+static s32 tas2783_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ unsigned long t;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ t = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(TAS2783_PROBE_TIMEOUT));
+ if (!t) {
+ dev_err(&slave->dev, "resume: initialization timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+ return -ETIMEDOUT;
+ }
+
+ slave->unattach_request = 0;
+
+regmap_sync:
+ regcache_cache_only(tas_dev->regmap, false);
+ regcache_sync(tas_dev->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops tas2783_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(tas2783_sdca_dev_system_suspend, tas2783_sdca_dev_resume)
+ RUNTIME_PM_OPS(tas2783_sdca_dev_suspend, tas2783_sdca_dev_resume, NULL)
+};
+
+static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ s32 ret;
+ u8 unique_id = tas_dev->sdw_peripheral->id.unique_id;
+
+ if (tas_dev->hw_init)
+ return 0;
+
+ tas_dev->fw_dl_task_done = false;
+ tas_dev->fw_dl_success = false;
+ scnprintf(tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname),
+ "tas2783-%01x.bin", unique_id);
+
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tas_dev->rca_binaryname, tas_dev->dev,
+ GFP_KERNEL, tas_dev, tas2783_fw_ready);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "firmware request failed for uid=%d, ret=%d\n",
+ unique_id, ret);
+ return ret;
+ }
+
+ ret = wait_event_timeout(tas_dev->fw_wait, tas_dev->fw_dl_task_done,
+ msecs_to_jiffies(TIMEOUT_FW_DL_MS));
+ if (!ret) {
+ dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
+ ret = -EAGAIN;
+ } else {
+ ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
+ ARRAY_SIZE(tas2783_init_seq));
+ tas_dev->hw_init = true;
+ }
+
+ return ret;
+}
+
+static s32 tas_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev);
+ struct device *dev = &slave->dev;
+
+ dev_dbg(dev, "Peripheral status = %s",
+ status == SDW_SLAVE_UNATTACHED ? "unattached" :
+ status == SDW_SLAVE_ATTACHED ? "attached" : "alert");
+
+ tas_dev->status = status;
+ if (status == SDW_SLAVE_UNATTACHED)
+ tas_dev->hw_init = false;
+
+ /* Perform initialization only if slave status
+ * is present and hw_init flag is false
+ */
+ if (tas_dev->hw_init || tas_dev->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* updated the cache data to device */
+ regcache_cache_only(tas_dev->regmap, false);
+ regcache_sync(tas_dev->regmap);
+
+ /* perform I/O transfers required for Slave initialization */
+ return tas_io_init(&slave->dev, slave);
+}
+
+static const struct sdw_slave_ops tas_sdw_ops = {
+ .read_prop = tas_read_prop,
+ .update_status = tas_update_status,
+};
+
+static void tas_remove(struct tas2783_prv *tas_dev)
+{
+ snd_soc_unregister_component(tas_dev->dev);
+}
+
+static s32 tas_sdw_probe(struct sdw_slave *peripheral,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+ struct device *dev = &peripheral->dev;
+ struct tas2783_prv *tas_dev;
+
+ tas_dev = devm_kzalloc(dev, sizeof(*tas_dev), GFP_KERNEL);
+ if (!tas_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed devm_kzalloc");
+
+ tas_dev->dev = dev;
+ tas_dev->sdw_peripheral = peripheral;
+ tas_dev->hw_init = false;
+ mutex_init(&tas_dev->calib_lock);
+ mutex_init(&tas_dev->pde_lock);
+
+ init_waitqueue_head(&tas_dev->fw_wait);
+ dev_set_drvdata(dev, tas_dev);
+ regmap = devm_regmap_init_sdw_mbq_cfg(peripheral,
+ &tas_regmap,
+ &tas2783_mbq_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed devm_regmap_init_sdw.");
+
+ /* keep in cache until the device is fully initialized */
+ regcache_cache_only(regmap, true);
+ tas_dev->regmap = regmap;
+ return tas_init(tas_dev);
+}
+
+static s32 tas_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&peripheral->dev);
+
+ pm_runtime_disable(tas_dev->dev);
+ tas_remove(tas_dev);
+ mutex_destroy(&tas_dev->calib_lock);
+ mutex_destroy(&tas_dev->pde_lock);
+ dev_set_drvdata(&peripheral->dev, NULL);
+
+ return 0;
+}
+
+static const struct sdw_device_id tas_sdw_id[] = {
+ /* chipid for the TAS2783 is 0x0000 */
+ SDW_SLAVE_ENTRY(0x0102, 0x0000, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, tas_sdw_id);
+
+static struct sdw_driver tas_sdw_driver = {
+ .driver = {
+ .name = "slave-tas2783",
+ .pm = pm_ptr(&tas2783_sdca_pm),
+ },
+ .probe = tas_sdw_probe,
+ .remove = tas_sdw_remove,
+ .ops = &tas_sdw_ops,
+ .id_table = tas_sdw_id,
+};
+module_sdw_driver(tas_sdw_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("ASoC TAS2783 SoundWire Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
new file mode 100644
index 000000000000..794333e0a350
--- /dev/null
+++ b/sound/soc/codecs/tas2783.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAS2783 Audio Smart Amplifier
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * The TAS2783 driver implements a flexible and configurable
+ * algo coefficient setting for single TAS2783 chips.
+ *
+ * Author: Niranjan H Y <niranjanhy@ti.com>
+ * Author: Baojun Xu <baojun.xu@ti.com>
+ */
+#include <linux/workqueue.h>
+
+#ifndef __TAS2783_H__
+#define __TAS2783_H__
+
+#define TAS2783_DEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200)
+#define TAS2783_DEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+/* book, page, register */
+#define TASDEV_REG_SDW(book, page, reg) (((book) * 256 * 128) + \
+ 0x800000 + ((page) * 128) + (reg))
+
+/* Volume control */
+#define TAS2783_DVC_LVL TASDEV_REG_SDW(0x0, 0x00, 0x1A)
+#define TAS2783_AMP_LEVEL TASDEV_REG_SDW(0x0, 0x00, 0x03)
+#define TAS2783_AMP_LEVEL_MASK GENMASK(5, 1)
+
+#define PRAM_ADDR_START TASDEV_REG_SDW(0x8c, 0x01, 0x8)
+#define PRAM_ADDR_END TASDEV_REG_SDW(0x8c, 0xff, 0x7f)
+#define YRAM_ADDR_START TASDEV_REG_SDW(0x00, 0x02, 0x8)
+#define YRAM_ADDR_END TASDEV_REG_SDW(0x00, 0x37, 0x7f)
+
+/* Calibration data */
+#define TAS2783_CAL_R0 TASDEV_REG_SDW(0, 0x16, 0x4C)
+#define TAS2783_CAL_INVR0 TASDEV_REG_SDW(0, 0x16, 0x5C)
+#define TAS2783_CAL_R0LOW TASDEV_REG_SDW(0, 0x16, 0x64)
+#define TAS2783_CAL_POWER TASDEV_REG_SDW(0, 0x15, 0x44)
+#define TAS2783_CAL_TLIM TASDEV_REG_SDW(0, 0x17, 0x58)
+
+/* TAS2783 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x01
+
+/* TAS2783 SDCA entity */
+
+#define TAS2783_SDCA_ENT_FU21 0x01
+#define TAS2783_SDCA_ENT_FU23 0x02
+#define TAS2783_SDCA_ENT_FU26 0x03
+#define TAS2783_SDCA_ENT_XU22 0x04
+#define TAS2783_SDCA_ENT_CS24 0x05
+#define TAS2783_SDCA_ENT_CS21 0x06
+#define TAS2783_SDCA_ENT_CS25 0x07
+#define TAS2783_SDCA_ENT_CS26 0x08
+#define TAS2783_SDCA_ENT_CS28 0x09
+#define TAS2783_SDCA_ENT_PDE23 0x0C
+#define TAS2783_SDCA_ENT_UDMPU23 0x0E
+#define TAS2783_SDCA_ENT_SAPU29 0x0F
+#define TAS2783_SDCA_ENT_PPU21 0x10
+#define TAS2783_SDCA_ENT_PPU26 0x11
+#define TAS2783_SDCA_ENT_TG23 0x12
+#define TAS2783_SDCA_ENT_IT21 0x13
+#define TAS2783_SDCA_ENT_IT29 0x14
+#define TAS2783_SDCA_ENT_IT26 0x15
+#define TAS2783_SDCA_ENT_IT28 0x16
+#define TAS2783_SDCA_ENT_OT24 0x17
+#define TAS2783_SDCA_ENT_OT23 0x18
+#define TAS2783_SDCA_ENT_OT25 0x19
+#define TAS2783_SDCA_ENT_OT28 0x1A
+#define TAS2783_SDCA_ENT_MU26 0x1b
+#define TAS2783_SDCA_ENT_OT127 0x1E
+#define TAS2783_SDCA_ENT_FU127 0x1F
+#define TAS2783_SDCA_ENT_CS127 0x20
+#define TAS2783_SDCA_ENT_MFPU21 0x22
+#define TAS2783_SDCA_ENT_MFPU26 0x23
+
+/* TAS2783 SDCA control */
+#define TAS2783_SDCA_CTL_REQ_POW_STATE 0x01
+#define TAS2783_SDCA_CTL_FU_MUTE 0x01
+#define TAS2783_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+#define TAS2783_DEVICE_CHANNEL_LEFT 1
+#define TAS2783_DEVICE_CHANNEL_RIGHT 2
+
+#define TAS2783_SDCA_POW_STATE_ON 0
+#define TAS2783_SDCA_POW_STATE_OFF 3
+
+/* calibration data */
+#define TAS2783_CALIB_PARAMS 6 /* 5 + 1 unique id */
+#define TAS2783_CALIB_MAX_SPK_COUNT 8
+#define TAS2783_CALIB_HDR_SZ 12
+#define TAS2783_CALIB_CRC_SZ 4
+#define TAS2783_CALIB_DATA_SZ ((TAS2783_CALIB_HDR_SZ) + TAS2783_CALIB_CRC_SZ + \
+ ((TAS2783_CALIB_PARAMS) * 4 * (TAS2783_CALIB_MAX_SPK_COUNT)))
+
+#if IS_ENABLED(CONFIG_SND_SOC_TAS2783_UTIL)
+int32_t tas25xx_register_misc(struct sdw_slave *peripheral);
+int32_t tas25xx_deregister_misc(void);
+#else
+static void tas25xx_register_misc(struct sdw_slave *peripheral) {}
+static void tas25xx_deregister_misc(void) {}
+#endif
+
+#endif /*__TAS2783_H__ */
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index 249ef5c4c762..b97c0e885713 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* TAS5086 ASoC codec driver
*
* Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
* TODO:
* - implement DAPM and input muxing
* - implement modulation limit
@@ -33,14 +24,13 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/of_gpio.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -255,21 +245,25 @@ struct tas5086_private {
/* Current sample rate for de-emphasis control */
int rate;
/* GPIO driving Reset pin, if any */
- int gpio_nreset;
+ struct gpio_desc *reset;
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_codec *codec)
+static int tas5086_set_deemph(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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)
+ 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);
@@ -278,10 +272,10 @@ static int tas5086_set_deemph(struct snd_soc_codec *codec)
static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
- ucontrol->value.enumerated.item[0] = priv->deemph;
+ ucontrol->value.integer.value[0] = priv->deemph;
return 0;
}
@@ -289,20 +283,20 @@ static int tas5086_get_deemph(struct snd_kcontrol *kcontrol,
static int tas5086_put_deemph(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
- priv->deemph = ucontrol->value.enumerated.item[0];
+ priv->deemph = ucontrol->value.integer.value[0];
- return tas5086_set_deemph(codec);
+ 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_codec *codec = codec_dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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:
@@ -319,12 +313,12 @@ static int tas5086_set_dai_sysclk(struct snd_soc_dai *codec_dai,
static int tas5086_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
- dev_err(codec->dev, "Invalid clocking mode\n");
+ if ((format & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
+ dev_err(component->dev, "Invalid clocking mode\n");
return -EINVAL;
}
@@ -357,8 +351,8 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int val;
int ret;
@@ -369,7 +363,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
ARRAY_SIZE(tas5086_sample_rates), priv->rate);
if (val < 0) {
- dev_err(codec->dev, "Invalid sample rate\n");
+ dev_err(component->dev, "Invalid sample rate\n");
return -EINVAL;
}
@@ -383,7 +377,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = index_in_array(tas5086_ratios, ARRAY_SIZE(tas5086_ratios),
priv->mclk / priv->rate);
if (val < 0) {
- dev_err(codec->dev, "Inavlid MCLK / Fs ratio\n");
+ dev_err(component->dev, "Invalid MCLK / Fs ratio\n");
return -EINVAL;
}
@@ -420,7 +414,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val = 0x06;
break;
default:
- dev_err(codec->dev, "Invalid DAI format\n");
+ dev_err(component->dev, "Invalid DAI format\n");
return -EINVAL;
}
@@ -436,7 +430,7 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
val += 2;
break;
default:
- dev_err(codec->dev, "Invalid bit width\n");
+ dev_err(component->dev, "Invalid bit width\n");
return -EINVAL;
}
@@ -450,13 +444,13 @@ static int tas5086_hw_params(struct snd_pcm_substream *substream,
if (ret < 0)
return ret;
- return tas5086_set_deemph(codec);
+ return tas5086_set_deemph(component);
}
static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
- struct snd_soc_codec *codec = dai->codec;
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_component *component = dai->component;
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
unsigned int val = 0;
if (mute)
@@ -467,11 +461,11 @@ static int tas5086_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
static void tas5086_reset(struct tas5086_private *priv)
{
- if (gpio_is_valid(priv->gpio_nreset)) {
+ if (priv->reset) {
/* Reset codec - minimum assertion time is 400ns */
- gpio_direction_output(priv->gpio_nreset, 0);
+ gpiod_set_value_cansleep(priv->reset, 1);
udelay(1);
- gpio_set_value(priv->gpio_nreset, 1);
+ gpiod_set_value_cansleep(priv->reset, 0);
/* Codec needs ~15ms to wake up */
msleep(15);
@@ -492,7 +486,7 @@ static int tas5086_init(struct device *dev, struct tas5086_private *priv)
/*
* 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 start under 'part 1'.
+ * all configured mid-z channels to start under 'part 1'.
*/
if (priv->pwm_start_mid_z)
regmap_write(priv->regmap, TAS5086_PWM_START,
@@ -769,9 +763,9 @@ static struct snd_soc_dai_driver tas5086_dai = {
};
#ifdef CONFIG_PM
-static int tas5086_soc_suspend(struct snd_soc_codec *codec)
+static int tas5086_soc_suspend(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int ret;
/* Shut down all channels */
@@ -784,9 +778,9 @@ static int tas5086_soc_suspend(struct snd_soc_codec *codec)
return 0;
}
-static int tas5086_soc_resume(struct snd_soc_codec *codec)
+static int tas5086_soc_resume(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
@@ -796,7 +790,7 @@ static int tas5086_soc_resume(struct snd_soc_codec *codec)
tas5086_reset(priv);
regcache_mark_dirty(priv->regmap);
- ret = tas5086_init(codec->dev, priv);
+ ret = tas5086_init(component->dev, priv);
if (ret < 0)
return ret;
@@ -819,22 +813,22 @@ static const struct of_device_id tas5086_dt_ids[] = {
MODULE_DEVICE_TABLE(of, tas5086_dt_ids);
#endif
-static int tas5086_probe(struct snd_soc_codec *codec)
+static int tas5086_probe(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to enable regulators: %d\n", ret);
+ 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), codec->dev)) {
- struct device_node *of_node = codec->dev->of_node;
+ 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);
@@ -845,13 +839,13 @@ static int tas5086_probe(struct snd_soc_codec *codec)
snprintf(name, sizeof(name),
"ti,mid-z-channel-%d", i + 1);
- if (of_get_property(of_node, name, NULL) != NULL)
+ if (of_property_read_bool(of_node, name))
priv->pwm_start_mid_z |= 1 << i;
}
}
tas5086_reset(priv);
- ret = tas5086_init(codec->dev, priv);
+ ret = tas5086_init(component->dev, priv);
if (ret < 0)
goto exit_disable_regulators;
@@ -868,20 +862,19 @@ exit_disable_regulators:
return ret;
}
-static int tas5086_remove(struct snd_soc_codec *codec)
+static void tas5086_remove(struct snd_soc_component *component)
{
- struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct tas5086_private *priv = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(priv->gpio_nreset))
+ if (priv->reset) {
/* Set codec to the reset state */
- gpio_set_value(priv->gpio_nreset, 0);
+ gpiod_set_value_cansleep(priv->reset, 1);
+ }
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
-
- return 0;
};
-static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
+static const struct snd_soc_component_driver soc_component_dev_tas5086 = {
.probe = tas5086_probe,
.remove = tas5086_remove,
.suspend = tas5086_soc_suspend,
@@ -892,10 +885,13 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.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 },
+ { "tas5086" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
@@ -914,12 +910,10 @@ static const struct regmap_config tas5086_regmap = {
.reg_write = tas5086_reg_write,
};
-static int tas5086_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+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);
@@ -945,16 +939,11 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
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;
+ /* Request line asserted */
+ priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->reset))
+ return PTR_ERR(priv->reset);
+ gpiod_set_consumer_name(priv->reset, "TAS5086 Reset");
ret = regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
if (ret < 0) {
@@ -979,22 +968,19 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
if (ret == 0)
- ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_tas5086,
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_component_dev_tas5086,
&tas5086_dai, 1);
return ret;
}
-static int tas5086_i2c_remove(struct i2c_client *i2c)
-{
- snd_soc_unregister_codec(&i2c->dev);
- return 0;
-}
+static void tas5086_i2c_remove(struct i2c_client *i2c)
+{}
static struct i2c_driver tas5086_i2c_driver = {
.driver = {
.name = "tas5086",
- .owner = THIS_MODULE,
.of_match_table = of_match_ptr(tas5086_dt_ids),
},
.id_table = tas5086_i2c_id,
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
new file mode 100644
index 000000000000..41d73f470f8b
--- /dev/null
+++ b/sound/soc/codecs/tas571x.c
@@ -0,0 +1,1090 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * TAS571x amplifier audio driver
+ *
+ * Copyright (C) 2015 Google, Inc.
+ * Copyright (c) 2013 Daniel Mack <zonque@gmail.com>
+ *
+ * TAS5721 support:
+ * Copyright (C) 2016 Petr Kulhavy, Barix AG <petr@barix.com>
+ *
+ * TAS5707 support:
+ * Copyright (C) 2018 Jerome Brunet, Baylibre SAS <jbrunet@baylibre.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/stddef.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/unaligned.h>
+
+#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 struct snd_kcontrol_new tas5733_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", TAS5733_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5733_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5733_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5733_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5733_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5733_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5733_CH1_BQ6_REG),
+ BIQUAD_COEFS("CH1 - Biquad 7", TAS5733_CH1_BQ7_REG),
+ BIQUAD_COEFS("CH1 - Biquad 8", TAS5733_CH1_BQ8_REG),
+ BIQUAD_COEFS("CH1 - Biquad 9", TAS5733_CH1_BQ9_REG),
+ BIQUAD_COEFS("CH1 - Biquad 10", TAS5733_CH1_BQ10_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5733_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5733_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5733_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5733_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5733_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5733_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5733_CH2_BQ6_REG),
+ BIQUAD_COEFS("CH2 - Biquad 7", TAS5733_CH2_BQ7_REG),
+ BIQUAD_COEFS("CH2 - Biquad 8", TAS5733_CH2_BQ8_REG),
+ BIQUAD_COEFS("CH2 - Biquad 9", TAS5733_CH2_BQ9_REG),
+ BIQUAD_COEFS("CH2 - Biquad 10", TAS5733_CH2_BQ10_REG),
+
+ BIQUAD_COEFS("CH1 - Cross Biquad 0", TAS5733_CH1_CBQ0_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 1", TAS5733_CH1_CBQ1_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 2", TAS5733_CH1_CBQ2_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 3", TAS5733_CH1_CBQ3_REG),
+
+ BIQUAD_COEFS("CH2 - Cross Biquad 0", TAS5733_CH2_CBQ0_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 1", TAS5733_CH2_CBQ1_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 2", TAS5733_CH2_CBQ2_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 3", TAS5733_CH2_CBQ3_REG),
+};
+
+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 = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
+ .regmap_config = &tas5733_regmap_config,
+ .vol_reg_size = 2,
+};
+
+static const struct reg_default tas5753_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x41},
+ {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, 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, 0x01},
+ {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_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x57},
+ {TAS571X_INPUT_MUX_REG, 0x00017772},
+ {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 tas5753_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5753_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5753_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5753_chip = {
+ .supply_names = tas5721_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5721_supply_names),
+ .controls = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
+ .regmap_config = &tas5753_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 int tas571x_i2c_probe(struct i2c_client *client)
+{
+ struct tas571x_private *priv;
+ struct device *dev = &client->dev;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+ i2c_set_clientdata(client, priv);
+
+ priv->chip = i2c_get_match_data(client);
+
+ 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, },
+ { .compatible = "ti,tas5753", .data = &tas5753_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 },
+ { "tas5753", (kernel_ulong_t) &tas5753_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 <cernekee@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
new file mode 100644
index 000000000000..2b3eff4023b9
--- /dev/null
+++ b/sound/soc/codecs/tas571x.h
@@ -0,0 +1,141 @@
+/* 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
+
+#define TAS5733_CH1_BQ0_REG 0x26
+#define TAS5733_CH1_BQ1_REG 0x27
+#define TAS5733_CH1_BQ2_REG 0x28
+#define TAS5733_CH1_BQ3_REG 0x29
+#define TAS5733_CH1_BQ4_REG 0x2a
+#define TAS5733_CH1_BQ5_REG 0x2b
+#define TAS5733_CH1_BQ6_REG 0x2c
+#define TAS5733_CH1_BQ7_REG 0x2d
+#define TAS5733_CH1_BQ8_REG 0x2e
+#define TAS5733_CH1_BQ9_REG 0x2f
+
+#define TAS5733_CH2_BQ0_REG 0x30
+#define TAS5733_CH2_BQ1_REG 0x31
+#define TAS5733_CH2_BQ2_REG 0x32
+#define TAS5733_CH2_BQ3_REG 0x33
+#define TAS5733_CH2_BQ4_REG 0x34
+#define TAS5733_CH2_BQ5_REG 0x35
+#define TAS5733_CH2_BQ6_REG 0x36
+#define TAS5733_CH2_BQ7_REG 0x37
+#define TAS5733_CH2_BQ8_REG 0x38
+#define TAS5733_CH2_BQ9_REG 0x39
+
+#define TAS5733_CH1_BQ10_REG 0x58
+#define TAS5733_CH1_CBQ0_REG 0x59
+#define TAS5733_CH1_CBQ1_REG 0x5a
+#define TAS5733_CH1_CBQ2_REG 0x5b
+#define TAS5733_CH1_CBQ3_REG 0x5c
+
+#define TAS5733_CH2_BQ10_REG 0x5d
+#define TAS5733_CH2_CBQ0_REG 0x5e
+#define TAS5733_CH2_CBQ1_REG 0x5f
+#define TAS5733_CH2_CBQ2_REG 0x60
+#define TAS5733_CH2_CBQ3_REG 0x61
+
+#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
new file mode 100644
index 000000000000..f0361822061f
--- /dev/null
+++ b/sound/soc/codecs/tas5720.c
@@ -0,0 +1,827 @@
+// 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 <dannenberg@ti.com>
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#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 {